We are migrating our existing Javascript Google login to new Google OAuth library, as specified in the deprecation notice here - https://developers.googleblog.com/2021/08/gsi-jsweb-deprecation.html
We are following this guide, and our javascript code looks as follows:
const client = google.accounts.oauth2.initTokenClient({
client_id: <our-client-id>,
scope: 'https://www.googleapis.com/auth/userinfo.email \
https://www.googleapis.com/auth/userinfo.profile',
callback: (tokenResponse: any) => {
if (tokenResponse && tokenResponse.access_token) {
if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile')) {
console.log(tokenResponse.access_token );
.. further auth logic
}
}
}
});
client.requestAccessToken();
Everything works fine in a sense that I am able to retrieve the token from Google, except that it looks like the JWT token generated by the new library is invalid. To be more precise, it does not match the JWT token specification - the token doesn't consist of 3 parts (header/payload/signature) and it's too short (meaning that it probably doesn't carry all the data it should). It also doesn't pass the validation in https://jwt.io/
Let me show you an example.
Token generated by the previous version of the library:
eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg2MTY0OWU0NTAzMTUzODNmNmI5ZDUxMGI3Y2Q0ZTkyMjZjM2NkODgiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy<...>1MWMifQ.OgOOhbTTbLYzcxNhNdvozf1<...>4bRLT3DILyWeO1FX64WaabGRjCR2amQ
Token generated by the new library:
ya29.A0ARrdaM_hHsX50Qim6c1NudaZvv6qMpMkbNxW-ltOh49s6U4JKZNvPJV3d0TAE_rqqvkvVG2983ryewiufhdsklKGiH-vEdIs7u2389iwek-dcgkY1SE-b21g0932oiweKT-ZKTJvmHAY
The issue is, therefore, that the new version of the library doesn't return proper JWT token (as described above). We cannot use this new token for anything, since we cannot verify it's integrity (who issued it, whether token secret matches our app id, when the token expires, etc...).
I also try to test this using the Google's own OAuth debug endpoint, but it also complaints that the second token is invalid:
https://oauth2.googleapis.com/tokeninfo?id_token=ya29.A0ARrdaM_hHsX50Qim6c1NudaZvv....
Note: Since JWT bears some private data, I stripped the base64 encoded part of the first token (the part after the first dot and before the second dot), same as the 3rd part (after the second dot). Since I cannot understand the last token, I also replaced some characters in there, but it should not affect the demonstration of this problem.
Has anybody encountered anything like this? We have an existing set of projects that need to be migrated to new JS library but it appears that it simply doesn't work. I tried creating a new project from scratch just to see if the issue is somehow only affecting old projects, but no success - the same broken token is returned by Google OAuth when using the new method.
Google OAuth Access Tokens are not JWTs. They are opaque binary values meaning you cannot directly extract information from the token. Google, however, can look up the token in its systems.
You can verify a Google OAuth Access Token by calling this endpoint:
curl -H 'Authorization: Bearer $ACCESS_TOKEN' https://www.googleapis.com/oauth2/v3/tokeninfo
Additional information for tokens created from user identities:
curl -H 'Authorization: Bearer $ACCESS_TOKEN' https://www.googleapis.com/oauth2/v3/userinfo
If the token is invalid, the request will fail.
The returned information will be JSON describing the token.
Related
I am requesting access token from Microsoft Graph using this procedure:
I request access to the following scopes:
User.Read.All openid profile email offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send
After the consent screen in web browser, the redirection occurs and the codes are sent to temporary localhost web server running on user's PC.
The code received is exchanged for access_token and refresh_token
When I try to query Microsoft Graph for user's profile I query:
GET https://graph.microsoft.com/v1.0/me
Header of the GET request contains:
Authorization: Bearer token-here-all-in-one-line
But I get the resulting JSON:
"InvalidAuthenticationToken"
"CompactToken parsing failed with error code: 8004920A"
I would normally assume the token is not correct, but I tested the same token from C++ app and a small PHP app, and I always test the same error. To be sure that it is not the wrong token, I deliberately modify it to a wrong token and then I get:
"CompactToken parsing failed with error code: 80049217"
After googling - 8004920A means "token rejected" (the error I have problem with) and 80049217 means "malformed token" so that is consistent with me deliberately inserting false data as token.
So I would assume that the token is correct but Microsoft Graph rejects it to query user profile information which is consented and approved.
I have tested the token on IMAP and SMTP access and there it works - mails are sent and received, so the access_token is definitely good.
Any ideas why Microsoft Graph rejects my attempt to query user profile?
Do I need to enable something when registering application in AzureAD portal?
I am doing this from C++ or from PHP so I don't think the code is of relevance here.
Not sure if this is the case, but it may be. You cannot use the same token for graph access and another API acccess (such as Outlook REST api, in your case). Meaning, you cannot mix scopes from different namespaces. Not sure why ("by design"?), you just can't. You need two separate tokens.
I faced similar issue with SharePoint REST api access (so may not be 100% sure it is also true for Outlook as well, but probably it is). I used a relatively easy way out - exchanging the token requested for openid profile email offline_access for token for https://outlook.office.com/... on my server (using the on_behalf_of flow).
If you don't really need the graph scope User.Read.All you could also try simply removing that one from your first authorization call that obtains the token.
As #Nikolay has stated, the tokens for Graph and Outlook can't be mixed. And Microsoft has designed it poorly so that IMAP.AccessAsUser.All for example can't be used for IMAP access - the access_token simply won't work unless the https://outlook.office.com/ prefix is added.
But - I found another way, which works for just reading the user's profile and email address which is by decoding the id_token. As Microsoft documentation states - openid profile and email scopes will work with both Graph and Outlook. profile token can be used to extract the information about user's profile.
As explained on:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
id_token contains information such as email address, name etc.
A sample ID token from their page is:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjFMVE16YWtpaGlSbGFfOHoyQkVKVlhlV01xbyJ9.eyJ2ZXIiOiIyLjAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vOTEyMjA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkL3YyLjAiLCJzdWIiOiJBQUFBQUFBQUFBQUFBQUFBQUFBQUFJa3pxRlZyU2FTYUZIeTc4MmJidGFRIiwiYXVkIjoiNmNiMDQwMTgtYTNmNS00NmE3LWI5OTUtOTQwYzc4ZjVhZWYzIiwiZXhwIjoxNTM2MzYxNDExLCJpYXQiOjE1MzYyNzQ3MTEsIm5iZiI6MTUzNjI3NDcxMSwibmFtZSI6IkFiZSBMaW5jb2xuIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiQWJlTGlAbWljcm9zb2Z0LmNvbSIsIm9pZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC02NmYzLTMzMzJlY2E3ZWE4MSIsInRpZCI6IjkxMjIwNDBkLTZjNjctNGM1Yi1iMTEyLTM2YTMwNGI2NmRhZCIsIm5vbmNlIjoiMTIzNTIzIiwiYWlvIjoiRGYyVVZYTDFpeCFsTUNXTVNPSkJjRmF0emNHZnZGR2hqS3Y4cTVnMHg3MzJkUjVNQjVCaXN2R1FPN1lXQnlqZDhpUURMcSFlR2JJRGFreXA1bW5PcmNkcUhlWVNubHRlcFFtUnA2QUlaOGpZIn0.1AFWW-Ck5nROwSlltm7GzZvDwUkqvhSQpm55TQsmVo9Y59cLhRXpvB8n-55HCr9Z6G_31_UbeUkoz612I2j_Sm9FFShSDDjoaLQr54CreGIJvjtmS3EkK9a7SJBbcpL1MpUtlfygow39tFjY7EVNW9plWUvRrTgVk7lYLprvfzw-CIqw3gHC-T7IK_m_xkr08INERBtaecwhTeN4chPC4W3jdmw_lIxzC48YoQ0dB1L9-ImX98Egypfrlbm0IBL5spFzL6JDZIRRJOu8vecJvj1mq-IUhGt0MacxX8jdxYLP-KUu2d9MbNKpCKJuZ7p8gwTL5B7NlUdh_dmSviPWrw
The token consists of 3 parts - header, payload and the signature. They are separated by a dot. That's a standard JWT (JSON Web Token) See details at - https://en.wikipedia.org/wiki/JSON_Web_Token
After separating them and then base64 decoding each (the signature doesn't give anything useful after decoding) the result is:
1) The header
{
"typ": "JWT",
"alg": "RS256",
"kid": "1LTMzakihiRla_8z2BEJVXeWMqo"
}
2) The payload
Payload which contains the user info. Additional information can be extracted by adding Optional claims when registering app in Azure AD - (Token configuration in the menu on the left).
{
"ver": "2.0",
"iss": "https://login.microsoftonline.com/9122040d-6c67-4c5b-b112-36a304b66dad/v2.0",
"sub": "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
"aud": "6cb04018-a3f5-46a7-b995-940c78f5aef3",
"exp": 1536361411,
"iat": 1536274711,
"nbf": 1536274711,
"name": "Abe Lincoln",
"preferred_username": "AbeLi#microsoft.com",
"oid": "00000000-0000-0000-66f3-3332eca7ea81",
"tid": "9122040d-6c67-4c5b-b112-36a304b66dad",
"nonce": "123523",
"aio": "Df2UVXL1ix!lMCWMSOJBcFatzcGfvFGhjKv8q5g0x732dR5MB5BisvGQO7YWByjd8iQDLq!eGbIDakyp5mnOrcdqHeYSnltepQmRp6AIZ8jY"
}
3) The signature
For verification purposes only.
1AFWW-Ck5nROwSlltm7GzZvDwUkqvhSQpm55TQsmVo9Y59cLhRXpvB8n-55HCr9Z6G_31_UbeUkoz612I2j_Sm9FFShSDDjoaLQr54CreGIJvjtmS3EkK9a7SJBbcpL1MpUtlfygow39tFjY7EVNW9plWUvRrTgVk7lYLprvfzw-CIqw3gHC-T7IK_m_xkr08INERBtaecwhTeN4chPC4W3jdmw_lIxzC48YoQ0dB1L9-ImX98Egypfrlbm0IBL5spFzL6JDZIRRJOu8vecJvj1mq-IUhGt0MacxX8jdxYLP-KUu2d9MbNKpCKJuZ7p8gwTL5B7NlUdh_dmSviPWrw
I am building a Django Rest Framework API which is using JWT authentication. I had created access tokens and refresh tokens and sent them to users.
I also have a refresh token endpoint that will take old refresh token and generate new pair of tokens and send to user.
I have doubt in its behavior related part. Currently what I can see that whenever I create new pair of access and refresh token using previous refresh token, the old access token is also working and new one is also working.
However once when I was using OAuth2.0 (in different case), I observed that in that case the old access token won't work if we had created new refreshed tokens.
But in case of my implementation of JWT in DRF this thing won't happens. I am not storing token in database.
So I want to know that is this any implementation specific problem or is it the property of JWT only, and if it is property then please share some details over it with me.
Thanks.
According to JWT introduction:
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
One of the value, that you can encode is 'exp' which states for expiration date. That's why your old tokens do not expire, cause they expiration date is still valid, and any other claims didn't change. Idea behind the 'refresh' token, is to provide new tokens with bigger exp value. Saying other way, you should not expect that the authorization will fail now, as the old token is still correct one.
As well you store nothing in the database (about this I also suggest to read answer provided by #sdoxsee
A simple API endpoint, with a Cognito User Pool Authorizer, when using the Authorizer Test button ( or using postman/Insomnia ) with a valid token fails ( Screenshot bellow ):
I know the token is valid as I can make a successful call to the Cognito user pool user-info end-point using the same token and get the desired response back.
Also removing the authorizer ( setting it to None ), will return the desired response as well.
I've tried both with and without Bearer ..., I've also tried to change the Authorization Token source to method.request.header.Authorization as I've found in some older Question but immediately after saving, it changes it back to Authorization, while method.response.header.Authorization doesn't seem to work either.
The API was deployed through a CloudFormation stack.
I'm writing a complete guide to this issue as the documentation is lacking and it's not easy to find the right information for such a simple task. but it may take a few days, so till then I'll post a short answer here and once ( hopefully ) I finish the guide I'll update this answer:
My problem was that I was using the access_token, but I had to use the identity_token! The other problem is that none of the OAuth2 tools available ( like Auth modules of Postman and Insomnia ) return or use the identity_token, they don't even show the token! and although it is in the OAuth2 specs, nobody's using it ( except Cognito! ),...
So I made some changes to the Insomnia source-code of the OAuth2 module and used the correct token, then it started working!
I'm going to make a PR to Insomnia and if it went through the feature may become available in next releases, otherwise, I'll make my packaged binary available so peep can use it since otherwise, it's a nightmare to get tokens from AWS!
If your API methods - do not have OAuth scopes: must use ID tokens.
If your API methods - have OAuth scopes: must use access tokens.
The test method always uses an id-token. So to use this in Postman - add an OAuth scope to your API methods.
From:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
With the COGNITO_USER_POOLS authorizer, if the OAuth Scopes option isn't specified, API Gateway treats the supplied token as an identity token and verifies the claimed identity against the one from the user pool. Otherwise, API Gateway treats the supplied token as an access token and verifies the access scopes that are claimed in the token against the authorization scopes declared on the method.
Strange but I thought I have a similar issue, but I was aware of that in the response there are two tokens and I was trying both and none of them was working.
After reading so many things around the internet, nothing was working.
I went to the ApiGateway user interface, clicked on the authorizer, then edit and I hit save. AND VOILA, it works. Strange, maybe it will help someone else too.
I had the same issue and I tried both id_token and access_token as well but didn't work.
Also tried to redeploy my stack, but didn't work.
I'm using AWS CDK to deploy my stack.
My solution was to go to the user interface, click on the authorizer -> edit -> save without changes. Then I ran the "test" and it worked.
Don't forget to deploy it. I forgot it and spent hours debugging why it is working in the test method, but not working using Postman.
I'm having problems with Get New Access Token for Postman and SmartSheet.
All URLS are prefixed with https:// but StackOverflow would not allow that.
Callback URL: www.getpostman.com/oauth2/callback]
Token Name: Test
Auth URL: app.smartsheet.com
Access Token URL: app.smartsheet.com/token
Client ID: used the one provided when registering my app with SmartSheet
Client Secret: used the one provided when registering my app with SmartSheet
Scope: blank
Grant Type: [Authorization Code]
When I click Request Token it takes me to the SmartSheet Login. After I login and close the SmartSheet browser I get Could not complete OAuth 2.0 Login.
Looking at your example the Auth URL is incorrect. That should be
https://app.smartsheet.com/b/authorize
Also, the Access Token URL should be
https://api.smartsheet.com/2.0/token
The Smartsheet OAuth2 flow also requires a Scope, so it can't be left blank in spite of what Postman says.
More information on all of this can be found in the documentation:
http://smartsheet-platform.github.io/api-docs/#oauth-flow
It is important to note that with all of this set correctly setting this up in Postman still won't work. This is due to the fact that the Smartsheet process of obtaining and refreshing the token Smartsheet requires clients to hash the authorization code (with a pipe and the app secret, using SHA256) rather than sending it in clear text. This is arguably non-standard, but is still within the OAuth2 spec. More information on this process is at the documentation I referenced above.
Unfortunately, it does not look like Postman supports these types of deviations from "vanilla" OAuth2. Depending on what you are trying to accomplish, you will either have to go though the steps of the process manually, or stand up a third-party app in a hosting environment. If you are simply looking to generate a token, this approach http://smartsheet-platform.github.io/api-docs/#direct-api-access may work for you instead.
If you are trying to test in Postman the Direct API approach works. http://smartsheet-platform.github.io/api-docs/#direct-api-access
Step 1) Go to your actual smartsheet "https":"//app.smartsheet.com/b/home" and under Account>Personal Settings>API Access -- Generate a token (copy it you wont be able to copy after you close)
Step 2) Get the url for your sheet. Right click on the sheet name tab and select Properties. Copy the the Sheet ID (ie 123456). Add it to the end of the url: "https":"//api.smartsheet.com/2.0/sheets/123456"
Step 3)The most confusing one in my opinion. In Postman select No Authorization. Then go and update the header with "Bearer 0da6cf0d848266b4cd32a6151b1". You have to have the word Bearer and the randomly generated string of numbers is from Step 1.
Then send the get request and you get your sheet back in json format.
I am using oauth.io to handle authentication in an Android app. I login using the service and then pass the access token to the server. As part of the server-side verification, I make a call to https://graph.facebook.com/debug_token?input_token={user access token}&access_token={app token}. I was receiving a response with the error message "(#100) The App_id in the input_token did not match the Viewing App".
I took this to mean that the app that generated the access token was not the same as the app that owns the app token I was sending in the request. Upon further inspection, I noticed that when I debugged the token with Facebook's tool (https://developers.facebook.com/tools/debug/accesstoken) I was seeing a different app id that belonged to oauth.io itself instead of my app. Since the app token is based on the app id and app secret, it obviously would not be correct if it was expecting oauth.io's app token.
Is there any way to continue using the debug_token endpoint through Facebook with a token generated by oauth.io?
Sorry this is a bit late - but it may still help you :) I had the same issue. In my perl code, I just did:
use LWP::Simple;
my $check_session_first = LWP::Simple::get("https://graph.facebook.com/me?access_token=$in->{token}");
if (!$check_session_first) {
print $IN->header;
print Links::SiteHTML::display('error', { error => qq|Sorry, we couldn't log you in. |});
return;
}
Basically, if $check_session_first is empty, then it means the session isn't valid. If its valid, it'll return a JSON object (which in my case, I process using the "JSON" perl module)