I am using the Microsoft Graph API along with Microsoft Authentication Library (MSAL) to acquire access tokens and I can successfully retrieve the access token, id token and refresh token. I can also successfully validate the id token. However, I cannot do the same for the access token as I'm getting this error:
raise InvalidSignatureError('Signature verification failed')
jwt.exceptions.InvalidSignatureError: Signature verification failed
I've reviewed as best as I can the microsoft documentation regarding validation here:
Microsoft identity platform access tokens
For validation, I can successfully decode using the jwt.ms site for jwt validation. So I know the tokens are good. I can see from the decode the claims and extract the aud(audience) and iss(issuer). These values are not the same for the id token (which I can successfully validate).
I am using the public keys from the following url as documented:
https://login.microsoftonline.com/<TENANT ID>/discovery/keys
So, what I missing in regards to validating the access token? (if I can validate the id token with no issues). How else can i troubleshoot this?
Jim's answer is correct and there are 2 use cases really - so it depends what you are trying to do:
Getting an access token for Microsoft resources - such as Graph - in which case you don't validate them
Getting a token for your own API resources, in which case you need to validate them. For this to work you need to 'expose an API scope' to get a different type of access token
Behaviour is not intuitive in my opinion, since I like to build standards based solutions. If it helps, here is a visual blog post of mine on getting the second scenario above working.
As far as I knew, we do not need to validate Microsoft graph signature. Because MsGraph recognized an opportunity to improve security for users. They achieved this by putting a ‘nonce’ into the jwt header. The JWS is signed with a SHA2 of the nonce, the nonce is replaced before the JWS is serialized. To Validate this token, the nonce will need to be replaced with the SHA2 of the nonce in the header. Now this can change since there is no public contract. So When calling Microsoft Graph, you should treat access tokens as opaque. For more details, please refer here and here
Related
My DocuSign App failed the "Go Live Review" due to me not using OAuth 2.0 authentication.
I have now read the documentation on how to "Get an access token with JWT Authentication" below:
https://developers.docusign.com/platform/auth/jwt/jwt-get-token/
Following this documentation I am able to get the JWT using jwt.io as suggested.
The Verified Signature code from jwt.io is then used to exchange for an DocuSign access_token using POSTMAN.
All this works as expected.
After some time the Verified Signature optained from jwt.io expires and calling https://account-d.docusign.com/oauth/token from POSTMAN results in this error:
{
"error": "invalid_grant",
"error_description": "expired_grant"
}
To fix this, I simply go to jwt.io and update the Payload section with new values for "iat" and "ext" and a new Verified Signature is obtained and copied in to POSTMAN for new request of access_token.
My question:
How to solve this expiring issue as DocuSign endpoint does not provide a refresh_token ?
It must be possible to somehow "renew" the Verified Signature as DocuSign endpoint requires this updated value for the field : "assertion" in POSTMAN.
I have tried to construct the three values separated by a period in the Verified Signature manually by base64 encoding, but I can´t figure out how to get the final RSASHA256 value in the Verified Signature.
I have seen the documentation for "Migrate to OAuth 2.0 using the DocuSign eSignature C#SDK" on this link :
https://developers.docusign.com/platform/auth/oauth2-requirements-migration/
Please note that I am not a developer and maybee the solution to my question is obvious for real developers - but I need a bit of help to solve this as I do not work in development-tool as shown on the video.
Please help...
How to solve this expiring issue as DocuSign endpoint does not provide a refresh_token?
The best is to create a new JWT for each OAuth call to DocuSign--and use software, not a website, to do so.
Note: Only obtain a new access token (by sending a fresh JWT) when the old access token has expired or is about to expire.
The DocuSign SDKs include functions to create a JWT. All of the SDKs are open source, so if you don't want to use the SDK as a whole, you can copy out the functions' implementations.
This would depend on what language your app is written in or what kind of tool it's built on. Generally, most programming languages have libraries that can generate the JWT for you without having to use jwt.io manually.
Does your app only make API calls via postman? This post might be helpful for you. It shows how to set up a pre-request script to generate the JWT
I have read some documentation on using ADFS. I understand it returns a token which is later passed by the client. In the diagrams, I can see that WebApi is validating the token, but I don't understand how. Maybe the api has access to the public key with which it verifies the token? If the api, after receiving the token, asked eg ADFS to confirm that the token is correct, I would understand, but it seems that it is not needed.
Images from: docs.microsoft
It is up to the application to validate the token.
Yes, it has access to the public key.
We have a number of services that can be called by users via HTTP based protocols (SOAP/REST/WebDAV). The services support various authentication mechanisms (e.g. Basic, and OIDC Bearer tokens). Sometimes a service has to call another service without a live user. For that, we configure technical users, and service A can call B with the credentials of a technical user.
For OAuth/OIDC authentication via Bearer tokens, we use the JWT bearer token flow to acquire access tokens for our technical users: Service A creates a JWT with the name of the technical user and signs it with its own private key. It then calls the OIDC token endpoint with that token and receives an access token for its technical user X. It can then use this token as a bearer token to call service B, which will accept the token for user X. (For more information on the JWT bearer flow, cf. RFC 7523 section 2.1 and RFC 7521 section 4).
This works fine with our Connect2id test server and also with Salesforce, but we haven't been able to get it working with Azure. Accessing Azure's token endpoint always results in the following error response:
AADSTS50027: JWT token is invalid or malformed
According the Microsoft's documentation, this error can have any number of reasons:
doesn't contain nonce claim, sub claim
subject identifier mismatch
duplicate claim in idToken claims
unexpected issuer
unexpected audience
not within its valid time range
token format is not proper
External ID token from issuer failed signature verification.
When trying to find out more about Azure and the JWT bearer flow, the only solid information I've been able to locate is this article:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
It describes the "On-Behalf-Of" (OBO) flow, which as far as I understand is an extension (by Microsoft?) to the JWT bearer flow, adding a second JWT to the mix along with a parameter called "requested_token_use" set to "on_behalf_of". I can only find "requested_token_use" in relation to the special OBO flow, and its only value seems to be "on_behalf_of".
OBO flow isn't what we want, since it seems to allow a service X to turn a token received by service Y into another token, using the Y token and a self-generated X token to call the token endpoint. Our use case only has a self-generated X token and the name of the user who the app is permitted to impersonate.
After a lengthy trial & error phase we've come to the impression that OBO may in fact be the only (non-standard) version of the JWT bearer flow that Azure knows about, and that it may not support the "normal" JWT bearer flow we're trying to use at all. Is this correct, and we have no option but to give up trying to solve this scenario with standard OIDC when Azure is involved, or does Azure in fact also support the flow we've implemented and we're just doing something wrong (e.g. wrong audience or issuer in our JWT, or a mis-configured app in Azure)?
PS: Our code is written in Java and we use the Nimbus library for our OAuth/OIDC communication.
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.
After weeks of tampering and diving into the depth i could clarify most of the issues as far as necessary for my use case.
Do I even have to bother with ADAL (or MSAL) then?
When using SSO, ADAL or MSAL are not required at all. I furthermore don't recommend using ADAL for new apps as it's EOS as of mid 2022.
On the getAuthToken() API:
It implicitly fetches a token through the client for a registered resource. You can check the method's source code for reference.
/** * Requests an Azure AD token to be issued on behalf of the app.
The token is acquired from the cache * if it is not expired.
Otherwise a request is sent to Azure AD to obtain a new token. *
#param authTokenRequest A set of values that configure the token
request. */
This token is i fact an access token, not an ID token. This makes sense if you consider the purpose of SSO: You are required to be logged into Teams for the call to succeed, you wouldn't require an ID token in that case.
The confusing lack of the nonce in the token's header is due to the token being cached. There is currently no work-around to this.
the callback never fails
This makes sense considering the way SSO works. It relies on the user being logged into teams. The seemingly only reason which could cause it to fail is when the user hasn't granted your app permission to retrieve an access token, which includes profile information from MS Graph.
(Though this is speculation, this appears to be the reason why all the samples use MS Graph as a resource for the regular flow). This obviously can't be easily reproduced as not everybody has a second tenant with admin permissions to test it.
As for reading the token through the middleware of .NET Core everything works as expected. You can check against more parameters including the signature through Jwt.Bearer.Extensions. However, keep in mind that you can't mitigate token replay as the token will lack the nonce.
I'm trying to validate access_token retrieved by Microsoft Identity Platform when token request is done (/common/oauth2/v2.0/token), but always I get an invalid format error on any validator even online ones like jwt.io. Do you know why?
My post from a while back may help you to understand the validation process.
A couple of common things to check:
Is there a 'nonce" field in the JWT header? If so it will fail validation and is not designed for your (custom) APIs to validate
Does token validation work if you use the Azure AD v1 endpoints?