OAUTH 2.0

Terms

  • Client - The application which requests for authentication

  • Resource Server - It's client's backend, Incase of confidential client, this keeps client_secret safe from public. It exchanges code for access tokens with authorization server, verifies the access token & sends the access token to Client.

  • Resource Owner - User requesting for a resource.

  • Authorization Server - Server authorizing the user to access a resource - Google, Facebook, Okta etc

OAUTH Parameters

  • client_id - Used to identify client uniquely by authorization server

  • client_secret - client_id & client_secret together is used to authenticate client by authorization server.client_secret needs to be kept secret for server side applications.

  • redirect_uri - client uri where user should be redirected after oauth grant is successful, this redirect url will receive authorization code & other important parameters such as state, which is sensitive.

  • response_type - response which client expects

    • token (Implicit Grant) - Directly client gets access & refresh token

    • code (Authorization Code Grant) - Client gets authorization code, code is exchanged for access & refresh token.

  • scope - permissions which user(access token) needs to be granted by authorization server like read, write, email

  • state - state param is used like a csrf check, to verify it's the same person who started the authorization request only completes the callback.

  • code - authorization code is sent in callback url for Authorization Code Grant flow which is later exchanged for access & refresh tokens.

  • code_challenge - Code challenge is used in PKCE grant type & is like a state param, but this is associated with particular authorization code, stored & validated by authorization server instead of client end.

  • code_verifier - code_verifier is used in PKCE request during exchange of authorization code for access & refresh tokens, The code verifier should match code_challenge sent during initial step.

  • code_challenge_method - Code challenge can be either plain or S256 (SHA256 of code challenge & base64 encode the hash).

  • grant_type -

    1. authorization_code - Authorization Code Grant Flow

    2. token - Implicit Grant Flow

    3. PKCE - similar to authorization grant flow, but also include code_challenge & code_verifier which acts like state param, but code_challenge is associated with particular authorization_code, stored & validated at authorization server end, instead of client end incase of state param.

    4. password - username & password provided by client is sent to authorization server in exchange for access & refresh token.

    5. client_credentials - client_id & client_secret is used to get access token, this is used to autheticate in client's auth context, not in user's context.

Implicit Grant Flow

  1. User access Client which needs authentication.

  2. Client redirects user to OAUTH Authorization server's /authorize endpoint.

  3. User gives consent & authorizes Client in permission prompt.

  4. After authorization is successful, user is redirected to callback url mentioned in redirect_uri parameter, with access_token appended to url fragment of callback url.

Note: This flow has become legacy, It's insecure as access_token is passed in url, it will be present in browser history. This method also doesn't have client_secret, Hence client authentication is also not done.

Authorization Code Grant Flow

  1. User access Client which needs authentication.

  2. Client redirects user to OAUTH Authorization server's /authorize endpoint.

  3. User gives consent & authorizes Client in permission prompt.

  4. After authorization is successful, user is redirected to callback url mentioned in redirect_uri parameter, with access_token appended to url fragment or as query parameter of callback url.

  5. Client sends authorization code to Resource Server(client's backend), mostly as a API request to backend.

  6. Resource Server keeps client_secret confidentially & uses authorization code, client_id & client_secret to obtain access token from authorization server by making a POST request to /token endpoint.

  7. Resource server obtains access & refresh token in response to /token request & sends that to Client as response to API request made in step 5.

Note: Here, as Resource Server(Client's backend) keeps client_secret confidentially, this is known as confidential client.

In some cases, native/mobile apps may need simple login feature without separate resource server, in that case client_secret is present in native application/mobile app itself. Native/Mobile app itself makes /token request & obtains access token & refresh directly. This scenario is called as public client.

It's suggested to use confidential client wherever possible, as this ensures that anyone having authorization code cannot directly exchange code for access token without client_secret.

This doesn't fully prevent authorization code injection attack - Attacker with code can send API request to resource server(client's backend) as in step 5 with authorization code & can still obtain access & refresh tokens.

Proof Key of Code Exchange (PKCE) Grant Flow

  1. User access Client which needs authentication.

  2. Client generates code_verifier & transforms to code_challenge(plain text or base64 encoding of SHA256 hash of code verifier)

  3. Client redirects user to OAUTH Authorization server's /authorize endpoint with code_challenge added to it's query params.

  4. User gives consent & authorizes Client in permission prompt.

  5. Authorization server associates code_challenge & code_challenge_method with authorization code for this particular OAUTH consent request.

  6. After authorization is successful, user is redirected to callback url mentioned in redirect_uri parameter, with access_token appended to url fragment or as query parameter of callback url.

  7. Client uses code to exchange for access token by making /token POST request to authorization server(incase of public client)with code_verifier added as query param .

  8. Authorization server uses associated code_challenge_method for the given authorization code to generate code_challenge & verifies if it matches with code_challenge initially associated as in step 5.

  9. Client obtains access & refresh token as response to /token POST made in step 7 if PKCE auth is successful.

Note: Although this is mostly used in public clients, this is strongly suggested in confidential clients also, as this method prevents Authorization Code Injection vulnerability.

Attacker with only code cannot obtain access token without proper code_verifier, as each code is uniquely associated with a code_challenge, which is validated by authorization server.

Client Credentials Grant Flow

Client makes request /token POST request with it's client_id & client_secret to obtain access & refresh tokens in context of client's authentication(not user's session).

Password Grant Flow

Client sends user's credentials in /token POST request along with client_id & client_secret to obtain access & refresh tokens of user.

Note: Password Grant Flow is highly not recommended as in this case, client also gets user credentials, which is the main reason OAUTH is invented(prevent exposure of user credentials to Clients).

Understanding CSRF attack in OAUTH

  1. Assume that Client supports 2 login functionalities - normal username/password & OAUTH login with social media account associated with the user.

  2. User needs to first normally login & then only integrate his social media account.

  3. Assume Client uses below request to integrate social media account of user.

  4. /OAUTHLinking API in backend makes /token request to authorization server & exchanges code for access & refresh token & redirects Client to callback url mentioned. (Confidential client)

  5. /AssociateToken API associates the access & refresh token obtained from query params with the currently active user's session.

  6. Given this scenario, attacker only needs to capture his own /OAUTHLinking request & send that to any user who is already logged in to client.This will automatically associate attacker's social media account with victim's session.

Note: This PortSwigger lab explains this scenario in a very detailed manner.

Flow without CSRF Protection:

  1. Client sends User to Authorization server

  2. Authorization Server after authorizing redirects user to client call back url along with authorization code in url fragment.

  3. Client uses code to exchange code for access_token.

Flow with CSRF Protection using state param:

  1. Client generates state param which is long & unique id & stores this locally.

  2. Client sends User to Authorization server with state param.

  3. Authorization Server after authorizing redirects user to client call back url along with authorization code & state param in url fragment.

  4. Client ensures that state param received from Authorization Server is same as one that's locally stored.

  5. Client uses code to exchange code for access_token.

In scenario with CSRF protection with state param, client ensures that it's the same person who has initiated the OAUTH request & also arrived at the callback url by validating the state param at client end.

Note: PKCE Grant flow also works in a similar fashion in preventing CSRF attack, only difference being code_challenge being validated by authorization server rather than at client end, as in case of state param used in Authorization Code Grant Flow.

It's suggested to use PKCE to prevent CSRF attack, as client doesn't need to handle state param validations, authorization server itself handles code_challenge validation providing CSRF protection by default.

Vulnerability Checklist

  1. Improper Session Validation by Resource Server - After Client gets access_token, it generally sends the access token along with other details such as email/userid to resource server in a API call to associate the access token with the user identifier(email/userid) & get a valid session(cookie). If resource server fails to properly validate from authorization server whether the access_token really belongs to the user identifier(email/userid) in the API request, attacker can use own access_token replacing user identifier(email/userid) to obtain victim's session. [PortSwigger Lab]

  2. Flawed CSRF Protection - This is already explained in detail earlier, This occurs because of missing or improper state param validation at client end, in case PKCE isn't used. [PortSwigger Lab]

  3. Open Redirect - If authorization_server doesn't properly validate redirect_uri callback parameter, attacker modify the parameter value to his own website, after the victim successfully authenticates, authorization code or access token is sent to attacker's website instead of client leading to account takeover in most cases. [PortSwigger Lab]

  4. Disclosure of client_secret in case of Confidential Client - If Confidential client is planned to use, but client_secret is disclosed by Client by either hardcoding somewhere or through network interception.

  5. Improper Validation of Scope by Resource Server - If attacker is able to modify scope param to elevate privilege & victim authenticates, Resource server/Attacker finally gets access token with elevated privilege, Resource server should properly validate & assign scope to user session without blindly trusting the access token recieved from authorization server. [PortSwigger Reference]

  6. Unverified user registration - Some OAuth authorization server provides option to regsiter themselves with username/email & password without proper email verification.This might lead to account takeover if attacker uses email id of victim to register in OAuth authorization server & use that to sign into Client where victim is already registered using Client's own authentication mechanism(email/password).

  7. No Access Token Expiry - Access token should be configured to expire in short span of time(60 mins), If necessary refresh token can be used to regenerate access token.

  8. Access Token Valid after User Logout - Access tokens should be revoked immediately if user choses to log out, at time's client doesn't revoke the access token even after logout.

Last updated