This has been giving me headaches for two weeks now and unfortunately neither similar issues nor the docs could enlighten me to full extend.
What I want to achieve
- I have a simple Teams App with a Web-API that contains data mapped to the user's Microsoft OID.
- The web API should expect access tokens and return a 401 error if the token is invalid.
- Thus the User should be able to sign in via the MS identity platform (through SSO for instance).
My understandings
I have set up the usual login process described here and can retrieve an access token and ID token through it. My assumption is that this access token could already be used to authorize with the web API, but expires after an hours or so and has to be re-fetched. This is what Silent Auth or SSO are good for, which appear to be mutually exclusive to me (SSO is easier to achieve but has more limited access).
If that's the case, will SSO suffice for my use case? and furthermore Do I even have to bother with ADAL (or MSAL) then? It's merely used for Silent Auth in the Teams Auth Sample.
If my assumptions are correct so far, I only have few steps left to achieve my goal:
I was taking the Goaltracker sample as reference. In the following extract SSO is used to retrieve a token and sets it in a header for the API. My flow is working similarly, but what bothers me is that the callback never fails, even when I log myself out. I'd suspect that getAuthToken()
method uses the id_token (implicitly?) stored by microsoft teams and uses it to retrieve an access token, which can only be done while signed in.
const authTokenRequest = {
successCallback: (token: string) => {
if (!config) {
config = axios.defaults;
}
config.headers["Authorization"] = `Bearer ${token}`;
resolve(config);
},
failureCallback: (error: string) => {
console.error("Error from getAuthToken: ", error);
window.location.href = "/signin";
},
resources: []
};
microsoftTeams.authentication.getAuthToken(authTokenRequest);
From what I can see from the Goaltracker (who doesn't seem to store any retrieved tokens at all), the microsoftTeams.authentication.authenticate()
method somehow stores the retrieved ID token itself and uses it to retrieve a new access token when required by calling microsoftTeams.authentication.getAuthToken()
. Is that how it works? The returned token's nature is elaborated here, though the answer itself does not clarify whether the retrieved token is an access toke or an ID token).
Further questions
Here is a good explanation on how an access token is different from an ID token (access token is for authorizing on a web API as a bearer token, while ID tokens verify the users identity in the client and should not be used for authorization). With that knowledge, looking at the authentication flow is somewhat confusing as it only labels a 'token' which is returned to the user (probably the ID token) and send to an API as a Bearer token, which would violate it's purpose. So when do each of these tokens come into play here?
Would my proposed implementation in the manner of Goaltracker work? Can i use the ID token to get my required OID and leave the rest to the Teams SDK?
What would be the most basic way to handle the API sided authorization? Such as merely checking whether the token belongs to user in a predefined subset of allowed tenants or if it is not expired yet.
EDIT (nov. 5): As for my last question I used
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = Configuration["api://{domainFromAppRegistration}/{tenant}"]
options.Authority = "https://sts.windows.net/{tenant}/";
});
And simple added the Authorize
attribute to my controller and it appears to work (I'm getting a 401 response when calling the API without token). Is that the correct way for multi-tenant applications? I just retrieved the parameters from an earlier access token.
EDIT (nov. 9): I am now aware that microsoft.teams.getAuthToken()
actually just returns an ID token for sure. The API also accepts only ID tokens issued by this method, and neither id tokens nor access tokens returned by the regular sign-in endpoint.
EDIT (nov. 17): Several attempts to use the tokens issued through regular sign-in and tampering with the token issuer (replacing the scope
="microsoft.graph" with resource
or scope
= {own resource}
didn't work. Setting the former as "openid" actually returned tokens but they were rejected by the authorization middleware.