Google Login Tutorial - Part 4
In part 3 of Google Login Tutorials we have discussed step three in implementing Google login process. Now in this part we will be discussing step four of implementing Google login process with OAuth 2.0
Step 4: Authenticating User
This is the main and most important step in our overall discussion. Authenticating the user involves obtaining a onetime authorization code from Google Servers and then an ID token and validating it. Note that ID token and access token is obtained by sending authentication request to Google authorization and token endpoints as we discussed initially.
What is ID token and access token?
ID tokens are a standardized feature of OpenID Connect designed for use in sharing identity assertions on the Internet. It is a JWT that contains identity information about the user that is digitally signed by Google.
Access token is a token that can be sent to a Google API and we can make use of various Google APIs from our application.
Note that there is a limit to the number of tokens per Google user account, and any authentication request above this limit might quietly invalidate the oldest outstanding token. For details, see Token expiration.
Now authenticating user involves following sub steps:
- Create an anti-forgery state token
- Send an authentication request to Google
- Confirm the anti-forgery state token
- Exchange code for access token and ID token
- Obtain user information from the ID token
- Authenticate the user
- Creating an anti-forgery state token :
The main purpose of this step is to ensure that the user is making the request and not a malicious attacker that is prevention from simple forgery attacks. In this sub step we need to create a unique session token that holds state between our application and the user’s client. We later match this unique session token with the authentication response returned by the Google OAuth Login service to verify that the user is making the request and not a malicious attacker. These tokens are often referred to as cross-site request forgery (CSRF) tokens.
How to choose state token?
One good choice for a state token is a string of 30 or so characters constructed using a high-quality random-number generator. Another is a hash generated by signing some of your session state variables with a key that is kept secret on your back-end.
The following code demonstrates generating unique session tokens using php.
// Create a state token to prevent request forgery.// Store it in the session for later validation.$state = md5(rand());$app['session']->set('state', $state);// Set the client ID, token state, and application name in the HTML while// serving it.return $app['twig']->render('index.html', array( 'CLIENT_ID' => CLIENT_ID, 'STATE' => $state, 'APPLICATION_NAME' => APPLICATION_NAME));
- Send an authentication request to Google
The next step is forming an HTTPS GET request with the appropriate URI and parameters and send this request, so that Google ca n handle the user authorization.
In this discussion we are using https://accounts.google.com/o/oauth2/auth which is Google’s Authorization End Point. Note that this URI may change at any time so, to keep updated with current working URIs related to Google’s OpenID Connect, we can make use of “The Discovery Document”.
What is Discovery Document?
It is just a JSON document found at a well-known location containing key-value pairs which provide details about the OpenID Connect provider’s configuration, including the URIs of the authorization, token, userinfo, and public-keys endpoints.
The Discovery document for Google’s OpenID Connect service may be retrieved from: https://accounts.google.com/.well-known/openid-configuration
Simply send GET request to this URI and we will get JSON object as response, for example currently for OpenID Connect Discovery 1.0 this response will be as show below:
{"issuer": "accounts.google.com","authorization_endpoint": "https://accounts.google.com/o/oauth2/auth","token_endpoint": "https://www.googleapis.com/oauth2/v3/token","userinfo_endpoint": "https://www.googleapis.com/plus/v1/people/me/openIdConnect","revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke","jwks_uri": "https://www.googleapis.com/oauth2/v2/certs","response_types_supported": ["code", "token", "id_token","code token", "code id_token", "token id_token", "code token id_token","none"],"subject_types_supported": [ "public" ],"id_token_alg_values_supported": [ "RS256" ]}
Now let’s come to the original point, that is now we need to send HTTP GET request with certain parameters to URI https://accounts.google.com/o/oauth2/auth
For getting one time authorization code and then ID token. So the parameters for this basic request will be as follows:
- client_id : which you obtain from the Developers Console.
- response_type : which in a basic request should be code. (Read more at response_type.)
- scope : in a basic request it should be openid email.
- redirect_uri : should be the HTTP endpoint (login handling Scripts URI as we specified in Google’s Developer Console while creating Proeject) on our server that will receive the response from Google. You specify this URI in the Developers Console.
- state : should include the value of the anti-forgery unique session token, as well as any other information needed to recover the context when the user returns to your application, e.g., the starting URL.
- login_hint : can be the user’s email address or the sub string, which is equivalent to the user’s Google ID. If you do not provide a login_hint and the user is currently logged in, the consent screen includes a request for approval to release the user’s email address to your app.
- Use the realm if you are migrating an existing application from OpenID 2.0 to OpenID Connect. For details, see Migrating off of OpenID 2.0.
- Use the hd parameter to limit sign-in to a particular Google Apps hosted domain. (Read more at hd.)
Note that only most commonly used parameters are listed above. For a complete list, plus more details about all the parameters, see Authentication URI parameters.
Here is an example of a complete OpenID Connect authentication URI:
https://accounts.google.com/o/oauth2/auth?client_id=424911365001.apps.googleusercontent.com&response_type=code&scope=openid%20email&redirect_uri=https://oa2cb.example.com/&state=security_token%3D138r5719ru3e1%26url%3Dhttps:/oa2cb.example.com/myHome&login_hint=jsmith@example.com&openid.realm=example.com&hd=example.com
Note the use of HTTPS rather than HTTP in all the steps of this process as HTTP connections are refused by Google servers. We should retrieve the base URI from the Discovery document using the key authorization_endpoint. The following discussion assumes the base URI is
For a basic request, specify the following parameters:
- client_id : which you obtain from the Developers Console.
- response_type : which in a basic request should be code. (Read more at response_type.)
- scope : which in a basic request should be openid email. (Read more at scope.)
- redirect_uri : should be the HTTP endpoint on your server that will receive the response from Google. You specify this URI in the Developers Console.
- state : should include the value of the anti-forgery unique session token, as well as any other information needed to recover the context when the user returns to your application, e.g., the starting URL. (Read more at state.)
- login_hint : can be the user’s email address or the sub string, which is equivalent to the user’s Google ID. If you do not provide a login_hint and the user is currently logged in, the consent screen includes a request for approval to release the user’s email address to your app. (Read more at login_hint.)
- Use the openid.realm if you are migrating an existing application from OpenID 2.0 to OpenID Connect. For details, see Migrating off of OpenID 2.0.
- Use the hd parameter to limit sign-in to a particular Google Apps hosted domain.
- Confirm anti-forgery state token
Now once Google done with its process, the response is sent to the redirect_uri that we have specified in the request. For our example request, all responses are returned in the query string, as shown below:
https://oa2cb.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps://oa2cb.example.com/myHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
Have look at it carefully. Now on the server, we must confirm that the state received from Google matches the session token we had created in sub step 1. This round-trip verification helps to ensure that the user, not a malicious script, is making the request and thus prevents our application from forgery attacks.
The following php code demonstrates confirming the session tokens that we created in sub step 1:
// Ensure that there is no request forgery going on, and that the user// sending us this connect request is the user that was supposed to.if ($request->get('state') != ($app['session']->get('state'))) { return new Response('Invalid state parameter', 401);}
- Request for Access Token and ID Token using code parameter obtained in previous sub step
The response we have obtained after sending request to the Google’s authorization end point includes a code parameter, a one-time authorization code that our server can exchange for requesting an access token and ID token from Google’s server or Google’s token endpoint. Now in this sub step our server makes this exchange by sending an HTTPS POST request to Google’s token end point having URI as mentioned in Discovery document. We assume the endpoint is https://www.googleapis.com/oauth2/v3/token. Now this POST request includes some standard parameters mentioned below:
- code : The authorization code that is returned from the previous request.
- client_id : The client ID that we obtain from the Google’s Developers Console, see step 1.
- client_secret : The client secret that you obtain from the Google’s Developers Console, see step 1.
- redirect_uri : The URI that we have specify in the Google’s Developers Console, see step 2.
- grant_type : This field must contain a value of authorization_code, as defined in the OAuth 2.0 specification.
A successful response to this request contains the following fields in a JSON array:
- access_token : A token that can be sent to a Google API.
- id_token : A JWT that contains identity information about the user that is digitally signed by Google.
- expires_in : The remaining lifetime of the access token.
- token_type : Identifies the type of token returned. At this time, this field always has the value Bearer.
- refresh_token (optional) : This field is only present if access_type=offline is included in the authentication request. For details, see Refresh tokens.
Stay tuned for next part of Google Login Tutorial.
Comments
Post a Comment