Search code examples
oauth-2.0google-oauthopenid-connect

Google OpenID connect oauth2 state parameter on the frontend. Is it secure?


I am currently learning about using OpenID Connect Oauth2 standard and authentication with Google. The documentation says an anti-forgery state token is used to verify that the user is making the request and not a malicious attacker.

We include this generated state token in the URL associated with the "Signin with Google" button that the user clicks. The URL looks like this:

https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=CLIENT_ID&
scope=openid%20email%20profile&
redirect_uri=appdomain.com&
state=**0Xgjymvk8tlPne45LPCnzfP3ofU5dm**&
nonce=0217322-3497425-1190558&
prompt=select_account

The the user accepts and Google redirects to REDIRECT_URI and adds state param and code param to the URL like this:

https://appdomain.com/?state=0Xgjymvk8tlPne45LPCnzfP3ofU5dm&code=4%2F0AbUR2VP....

Those state params all appear in the front end. Is it secure? Is it even ok to validate them by checking if they are the same in the frontend js code? I dont think so...

Do we need to create another link attached to the "Signin with Google" button pointing to a backend service that will generate the state token and then redirect the user to the first URL that will let him accept to sign in with Google? Then check if the state token received from Google from the url param on the frontend by sending it to the backend service that will authenticate the user to check that it is the same before authenticating the user?

What do you think about this?

Thank you!


Solution

  • The document you linked states:

    This document describes how to perform the server flow for authenticating the user. The implicit flow is significantly more complicated because of security risks in handling and using tokens on the client side. If you need to implement an implicit flow, we highly recommend using Google Identity Services.

    So, you should be generating the state from the backend, and then checking the state on the backend as well. Again, from the doc you linked it specifically says check the state on the server:

    On the server, you must confirm that the state received from Google matches the session token you created in Step 1. This round-trip verification helps to ensure that the user, not a malicious script, is making the request.

    Still, if you want to learn more... You should have a look at the OAuth 2.0 spec: OAuth 2.0 - State & CSRF

    The important part is this:

    A CSRF attack against the client's redirection URI allows an attacker to inject its own authorization code or access token, which can result in the client using an access token associated with the attacker's protected resources rather than the victim's (e.g., save the victim's bank account information to a protected resource controlled by the attacker).

    If you had a client-side-only app (aka SPA) using the Authorization Code Flow (with PKCE!), the attack is that a malicious actor gets the user to return to the redirect URI of the app with its own authorization code, the code that would get tokens for access into the malicious actor's account.

    Normally, the app would just take this authorization code and send it to the token endpoint to exchange it for access tokens. Then, the user thinks she is logged into her own account, but really is logged into the malicious actor's account and unwittingly hands over credit card info when she updates what she believes to be her account.

    With a sufficiently strong state value, the app first checks, whenever it gets an authorization code, the state value. If it's not there or it doesn't match what it had sent early in the authorization request, it knows that this authorization code is not tied to the authorization request it initiated. So, it knows not to send a token request.

    Here's also a brief explanation with links to more resources from OWASP: OWASP - CSRF This link in particular is very helpful for various types of CSRF like in client-side apps: OWASP - CSRF Cheat Sheet