How can I verify a Google authentication access token?
I need to somehow query Google and ask: Is [given access token] valid for the [example#example.com] Google account?
Short version
It's clear how an access token supplied through the Google Authentication Api :: OAuth Authentication for Web Applications can be used to then request data from a range of Google services. It is not clear how to check if a given access token is valid for a given Google account. I'd like to know how.
Long version
I'm developing an API that uses token-based authentication. A token will be returned upon provision of a valid username+password or upon provision of a third-party token from any one of N verifiable services.
One of the third-party services will be Google, allowing a user to authenticate against my service using their Google account. This will later be extended to include Yahoo accounts, trusted OpenID providers and so on.
Schematic example of Google-based access:
The 'API' entity is under my full control. The 'public interface' entity is any web- or desktop-based app. Some public interfaces are under my control, others will not be and others still I may never even know about.
Therefore I cannot trust the token supplied to the API in step 3. This will be supplied along with the corresponding Google account email address.
I need to somehow query Google and ask: Is this access token valid for example#example.com?
In this case, example#example.com is the Google account unique identifier - the email address someone uses to log in to their Google account. This cannot be assumed to be a Gmail address - someone can have a Google account without having a Gmail account.
The Google documentation clearly states how, with an access token, data can be retrieved from a number of Google services. Nothing seems to state how you can check if a given access token is valid in the first place.
Update
The token is valid for N Google services. I can't try a token against a Google service as means of verifying it as I won't know which subset of all Google's services a given user actually uses.
Furthermore, I'll never be using the Google authentication access token to access any Google services, merely as a means of verifying a supposed Google user actually is who they say they are. If there is another way of doing this I'm happy to try.
For user check, just post
get the access token as accessToken and post it and get the response
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=accessToken
you can try in address bar in browsers too, use httppost and response in java also
response will be like
{
"issued_to": "xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"audience": "xxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"user_id": "xxxxxxxxxxxxxxxxxxxxxxx",
"scope": "https://www.googleapis.com/auth/userinfo.profile https://gdata.youtube.com",
"expires_in": 3340,
"access_type": "offline"
}
The scope is the given permission of the accessToken. you can check the scope ids in this link
Update:
New API
post as below
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
Response will be as
{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser#gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}
For more info, https://developers.google.com/identity/sign-in/android/backend-auth
Ok, most answers are valid but not quite right. The idea of JWT is that you can validate the token without the need to contact the issuer everytime. You must check the id and verify the signature of the token with the known public key of the certificate google used to sign the token.
See the next post why and how to do this.
http://ncona.com/2015/02/consuming-a-google-id-token-from-a-server/
you can verify a Google authentication access token by using this endpoint:
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=<access_token>
This is Google V3 OAuth AccessToken validating endpoint, you can refer from google document below: (In OAUTH 2.0 ENDPOINTS Tab)
https://developers.google.com/identity/protocols/OAuth2UserAgent#validate-access-token
function authenticate_google_OAuthtoken($user_id)
{
$access_token = google_get_user_token($user_id); // get existing token from DB
$redirecturl = $Google_Permissions->redirecturl;
$client_id = $Google_Permissions->client_id;
$client_secret = $Google_Permissions->client_secret;
$redirect_uri = $Google_Permissions->redirect_uri;
$max_results = $Google_Permissions->max_results;
$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$access_token;
$response_contacts = curl_get_responce_contents($url);
$response = (json_decode($response_contacts));
if(isset($response->issued_to))
{
return true;
}
else if(isset($response->error))
{
return false;
}
}
Use the below endpoint to get user info such as name, email, photo etc.
https://www.googleapis.com/oauth2/v3/userinfo?access_token=<access token>
Use the below endpoint to get token info, such as expiry time, token scope etc.
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=<access token>
Google oauth code flow response in addition to access_token also returns id_token that contains useful for validation info in encrypted form.
One thing that makes ID tokens useful is that fact that you can pass
them around different components of your app. These components can use
an ID token as a lightweight authentication mechanism authenticating
the app and the user. But before you can use the information in the ID
token or rely on it as an assertion that the user has authenticated,
you must validate it.
Validation of an ID token requires several steps:
Verify that the ID token is a JWT which is properly signed with an appropriate Google public key.
Verify that the value of aud in the ID token is equal to your app’s client ID.
Verify that the value of iss in the ID token is equal to accounts.google.com or https://accounts.google.com.
Verify that the expiry time (exp) of the ID token has not passed.
If you passed a hd parameter in the request, verify that the ID token has a hd claim that matches your Google Apps hosted domain.
https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken link has code samples for validation of ID tokens.
See also https://security.stackexchange.com/questions/37818/why-use-openid-connect-instead-of-plain-oauth.
As per Google's documentation, you should use Google's AP Client Library that makes this (token verification, claim extraction etc.) much easier than writing your own custom code.
From a performance perspective, the token should be parsed locally without making a call to Google again. Off-course Google's public key is needed and retrieval of that key is done using a caching strategy, implemented in the Google's client library from #1 above.
FYI only. Google also uses a JWT token. See image below for reference.
Here's an example using Guzzle:
/**
* #param string $accessToken JSON-encoded access token as returned by \Google_Client->getAccessToken() or raw access token
* #return array|false False if token is invalid or array in the form
*
* array (
* 'issued_to' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
* 'audience' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
* 'scope' => 'https://www.googleapis.com/auth/calendar',
* 'expires_in' => 3350,
* 'access_type' => 'offline',
* )
*/
public static function tokenInfo($accessToken) {
if(!strlen($accessToken)) {
return false;
}
if($accessToken[0] === '{') {
$accessToken = json_decode($accessToken)->access_token;
}
$guzzle = new \GuzzleHttp\Client();
try {
$resp = $guzzle->get('https://www.googleapis.com/oauth2/v1/tokeninfo', [
'query' => ['access_token' => $accessToken],
]);
} catch(ClientException $ex) {
return false;
}
return $resp->json();
}
I need to somehow query Google and ask: Is this access token valid for example#example.com?
No. All you need is request standard login with Federated Login for Google Account Users from your API domain. And only after that you could compare "persistent user ID" with one you have from 'public interface'.
The value of realm is used on the Google Federated Login page to identify the requesting site to the user. It is also used to determine the value of the persistent user ID returned by Google.
So you need be from same domain as 'public interface'.
And do not forget that user needs to be sure that your API could be trusted ;) So Google will ask user if it allows you to check for his identity.
Try making an OAuth-authenticated request using your token to https://www.google.com/accounts/AuthSubTokenInfo. This is only documented to work for AuthSub, but it works for OAuth too. It won't tell you which user the token is for, but it will tell you which services it's valid for, and the request will fail if the token is invalid or has been revoked.
An arbitrary OAuth access token can't be used for authentication, because the meaning of the token is outside of the OAuth Core spec. It could be intended for a single use or narrow expiration window, or it could provide access which the user doesn't want to give. It's also opaque, and the OAuth consumer which obtained it might never have seen any type of user identifier.
An OAuth service provider and one or more consumers could easily use OAuth to provide a verifiable authentication token, and there are proposals and ideas to do this out there, but an arbitrary service provider speaking only OAuth Core can't provide this without other co-ordination with a consumer. The Google-specific AuthSubTokenInfo REST method, along with the user's identifier, is close, but it isn't suitable, either, since it could invalidate the token, or the token could be expired.
If your Google ID is an OpenId identifier, and your 'public interface' is either a web app or can call up the user's browser, then you should probably use Google's OpenID OP.
OpenID consists of just sending the user to the OP and getting a signed assertion back. The interaction is solely for the benefit of the RP. There is no long-lived token or other user-specific handle which could be used to indicate that a RP has successfully authenticated a user with an OP.
One way to verify a previous authentication against an OpenID identifier is to just perform authentication again, assuming the same user-agent is being used. The OP should be able to return a positive assertion without user interaction (by verifying a cookie or client cert, for example). The OP is free to require another user interaction, and probably will if the authentication request is coming from another domain (my OP gives me the option to re-authenticate this particular RP without interacting in the future). And in Google's case, the UI that the user went through to get the OAuth token might not use the same session identifier, so the user will have to re-authenticate. But in any case, you'll be able to assert the identity.
Check below URL. It works well. Its official document from Google itself.
Using one of the Google API Client Libraries (e.g. Java, Node.js, PHP, Python) is the recommended way to validate Google ID tokens.
https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library
Related
Just reading the docs, they seem very similar to me so I can't really discern why to use one over the other. Although identity token seems better since it has custom attributes on it from the user pool (eg: custom:blah and the default ones like name and email).
Right now, I am working with an app that passes the access token back down to the browser so it can use it for making ajax REST calls (there is an auth filter that expects this access token and validates it). Could I just switch out the access token with the id token? The current validation logic is to just get the sub field (the uuid) from the access token, but this sub field is also present in the identity token (as well as practically every other attribute except the aud which I don't need). I just want to make sure I am understanding this right as it is confusing to me why both tokens exist and seem so similar.
The id_token is for your application to process, so you can get all the personal details for your user, like their name, age, email address etc. Generally speaking you shouldn't send this token anywhere else as it contains sensitive user data.
The access_token is used to call other 'external' services (and by external I include other AWS services - these are often called over http). It provides service access authorisation for your user without having to include their personal details.
On the face of it this appears slightly confusing as you can actually use the id_token to access services in the same way as the access_token. However, good practise is to use the access_token in this circumstance and if backend services need user data, they should look it up themselves in Cognito.
EDIT: If you need to authenticate an api call based on claims in the identity token, there are circumstances when this is perfectly valid. But be aware of what details are in the identity token, and whether those claims are suitable to send to the particular API. If you don't need to use any claims from the id_token, use the access_token as this reduces the amount of potentially sensitive data you are sending.
The thing that wasn't obvious from documentation for me about the difference:
If you are using pretoken trigger function and want to add additional information to the claims with claimsToAddOrOverride , you need to use an id token because this information doesn't exist in the access token.
For ex:
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
'userProfileID': id,
}
},
}
I've expected it in the AppSync resolver with lambda function as source
Speaking about AWS User Pool tokens:
Identity token is used to authenticate users to your resource servers or server applications. For example, if you use Cognito as authorizer in AWS API Gateway you need to use Identity token to call API.
The purpose of the access token is to authorize API operations in the context of the user in the user pool. For example, you can use the access token to grant your user access to add, change, or delete user attributes.
The header for the access token has the same structure as the ID token. However, the key ID (kid) is different because different keys are used to sign ID tokens and access tokens.
I'm trying to implement the Azure AD OAuth authentication for our Django app and I would be doing that with Azure AD as an OAuth provider. So now I wanted to know how securely we can store the OAuth access/refresh tokens in the DB that we receive from Azure AD or any OAuth provider.
I want to store the user's access token in DB because we have a feature in our web app where users can send an email with their email ID and we have a periodic job that runs every half an hour and it's gonna fetch user's mails based on a specific subject line. This we're gonna do with the help of Microsoft's Graph API and in order to call Microsoft Graph API, the web app should store the user's access token may be in the DB. But my concern is once we receive the access and refresh token, it shouldn't be accessed by anyone once we store it in the DB. So how securely or in an encrypted way we can store the OAuth2 access tokens in Django.
I have gone through a few articles, QnA, and forums on this concern but wanted to hear from the Django community as well.
Thanks in advance.
Let's start from sending email by graph(https://graph.microsoft.com/v1.0/users/{userId}/sendMail). I've done some test, here's the detail.
When I used credential flow to generate an access token, I can't use it to send email with an error like 'ErrorAccessDenied', it means that we can't generate a token that can be used for many accounts.
When I used auth code flow to generate an access token, I can't use it to send email when I set a different user id with the id that used to generate token in the api url. In this scenario, I also get the same error as above.
When I used ropc flow and I can send email successfully with it, this means I will send email successfully only when I used the correct user id and token.
Error message when failed to send email:
{
"error":{
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again."
}
}
And according to the test result, I think if you decide to store the tokens into the database, you can save it inline with the user id, so when your periodic job executed, your program can query for the correct token which could be decoded first to check if it is expired, and use it to send email.
If I misunderstand in some place, pls point my error out, thanks.
I'm sorry for what appears to be somewhat of a dumb question, but I'm playing around in AWS' Web Identity Federation playground and am not clear what type of access (authorization) token I am receiving when I first log in and the provider redirects to the callback URL. I first log in with my Amazon.com account, and Amazon supplies me with an access token. I understand that I will trade this in for an STS token so I can assume a specific role defined for me, but I wanted to inspect the token using the jwt.io site:
This all makes sense to me, but when I paste it into the jwt.io form to see its plaintext contents, I get an Invalid Signature error. If this is not a JWT, where is the information for access scopes (like profile and expiration windows (like 3600) stored then?
I'm guessing this means that the access token is not a JWT token, but rather only a simple string combination that has no real-world meaning and
basically says "Check this token is valid and then grant whoever gave you this token the permissions associated with it"? Is this always true for every web identity provider (Amazon, Google, Facebook, etc.)? Does the fact that token_type=bearer in the query string of the redirect URL mean that it cannot be a JWT?
If jwt.io says Invalid Signature, it’s not necessarily because the token is not valid, it is probably because you have not provided jwt.io with a valid signing key. In the signature box on the right, paste the signing key.
This is a problem we are confronted with today. I looked online as much as I can before deciding to ask the question here.
The question is, when a user makes a "Access Token Request" with a username/password, should the Access Token response contain "user-id" field?
The OAuth 2.0 spec says there can be an extra field (search for "example_parameter" in the spec) as part of the access token response. Can this extra field be user-id?
However, Many of the companies like facebook/google/twitter does not provide user-id as part of the access token response. User-id is provided as part of the validate token response.
What are the reasons for not providing user-id as part of the Access Token response? Why do we have to make another call (an extra round trip) if we need a user-id? What are the consequences of providing user-id as part of the Access Token response?
Most applications don't want to leak their user-ids to the UI layer. It is generally considered bad practice. If you send the userid to the UI layer, the UI will have the ability to request information about that UserId, and get information about it back...
This presents a security issue, as the client can generally modify these calls to get information about other UserIDs... If you are going to authenticate each call each time to validate that the token is bound to that user id, then it likely is a waste to have the client pass the userid itself, you are already doing the lookup, so you might as well keep the data internal to the service.
This also sets you up nicely to change your concept of a UserId in the future, maybe changing it to be group id, or an email address instead of the database userid...
Oauth2.0 is an authorisation framework and doesn't deal with user authentication.
The question is, when a user makes a "Access Token Request" with a
username/password, should the Access Token response contain "user-id"
field? In this context the resource
It's really a client requesting an access token and in many cases a client doesn't want to know about the id of the resource owner it just wants a token to allow it it call an endpoint on the resource server. The resource server can identity the user from the contents of the access token.
OpenID Connect can be used to extend OAuth2.0 to include an ID token. This gives basic user info in a standard format without the need to call a further endpoint.
An access token is a normally a bearer token that allows you to call an API. In OpenID Connect, you use the access token to call an api called the user_info endpoint, which returns a JSON object that contains information about the person, like first name, last name, etc. If you are looking for a free open source central authentication server that supports OAuth2, you should look at the Gluu Server http://gluu.org
I am implementing JavaScript based Google or Facebook OAuth for a client login system, where I hope to use the Access Token and Email Id combination returned from Google/Facebook after successful authentication of the user to create session on the Server. I have a .NET based server application. I am hoping to send this Access Token and Email Id combination using some AJAX methods like $.ajax and creating a session for this email id on the server end.
Now I have a limitation, my server is behind a firewall and it cannot contact Google/Facebook to validate the access token.
One of my colleague pointed me out that as there is no communication between my Server and Google/Facebook, any malicious user can send me user A's email id with any random access token, and I have no way to check the validity of this token whether it actually belongs to User A.
My server is using SSL communication. So, how can I make this scenario secure.
I don’t think you should use a token you can’t validate. I don’t think your app can be run if you can’t contact IDPs to validate tokens. It’s a bit surprising, most firewalls let you call out, they don’t let others call in.
You could request an ID token as described here Cloud endpoints oauth2 error
This contains a signed user ID and email in a JWT.
This article might help https://support.zendesk.com/entries/23675367-Setting-up-single-sign-on-with-JWT-JSON-Web-Token-
disclaimer: I haven't tried any of this personally. Also, as Tim says, are you sure you can't call out from behind your firewall?