OAuth 2.0 nature and purpose

How does it work?

Untrusted client. Some mobile applications doesn’t have a backend for OAuth 2.0, so the client part of the protocol flow goes on the mobile device.

Redirections from a browser to a mobile app behave differently depending on the system settings, the order in which applications installed and other magic.



Mobile application is a public client

client_id

,

, client_secret. client_id

client_secret

[steps A-C] Receive an authorization_code (hereinafter, code ).

[steps D-E] Exchange code to access_token .

Get resource via access_token .

code

[Step A] Client redirects the user to the service provider.

[Step B] Service provider requests permission from the user to provide the client with the data (arrow B up). The user provides data access (arrow B to the right).

[Step C] Service provider returns code to the user browser which redirects code to the client.



access_token

[Step D] Client server sends a request for access_token . Code , client_secret and redirect_uri are included in the request.

[Step E] In case of valid code , client_secret and redirect_uri , access_token is provided.



access_token

access_token

[steps 1-4 in the picture] Get code . [steps 5-6 in the picture] Exchange code to access_token Gain resource access via access_token

client_secret

client_secret

client_secret

client_secret

access_token

access_token

Redirection on mobile devices

Every service client must undergo the verification procedure.

Android users can turn AppLink off for a specific app in settings.

Android versions older than 6.0 and iOS versions older than 9.0 don’t support AppLink.



OK, what is there to attack?

Authorization Code Interception Attack

code

code

code

access_token

code_verifier

code_challenge

t(code_verifier)

code_challenge_method

t_m

Code_verifier

code_verifier

code

Code_challenge_method

Code_challenge

code_verifier

code_challenge_method

code_verifier

code_challenge

code_verifier

code

Client generates code_verifier and memorizes it.

Client chooses code_challenge_method and receives code_challenge from code_verifier .

[Step А] Client requests code , with code_challenge and code_challenge_method added to the request.

[Step B] Provider stores code_challenge and code_challenge_method on the server and returns code to a client.

[Step C] Client requests access_token , with code_verifier added to it.

Provider receives code_challenge from incoming code_verifier , and then compares it to code_challenge , that it saved.

[Step D] If the values match, the provider gives client access_token .



code_challenge

First, legitimate app requests code ( code_challenge and code_challenge_method are sent together with the request).

Malicious app intercepts code (but not code_challenge , since code _challenge is not in the response).

Malicious app requests access_token (with valid code , but without valid code_verifier ).

The server notices mismatch of code_challenge and raises an error message.



code_verifier

code_challenge

code_challenge

access_token

code

OAuth 2.0 CSRF

attacker@provider.com

code

com.taxi.app://oauth? code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4

access_token

state

Client application generates and saves CSRF token on a client’s mobile device.

Client application includes the CSRF token in code access request.

Server returns the same CSRF token with code in its response.

Client application compares the incoming and saved CSRF tokens. If their values match, the process goes on.



access_token

Hardcoded client secret

client_id

client_secret

client_id

client_secret

client_id

client_secret

Malicious app acting as a legitimate client

Other attacks

redirect_uri

How to do it securely?

Good, bad OAuth 2.0

The user enters his login and password for the service provider account in the app, that can easily steal this data.

OAuth 2.0 was initially developed to not to enter the service provider login and password.



The user gets used to entering his login and password anywhere thus increasing a fishing possibility.



Secure mobile OAuth 2.0 scheme

code_challenge

state

https://o2.mail.ru/code? redirect_uri=com.mail.cloud.app%3A%2F%2Foauth& state=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24& code_challenge=ZjYxNzQ4ZjI4YjdkNWRmZjg4MWQ1N2FkZjQzNGVkODE1YTRhNjViNjJjMGY5MGJjNzdiOGEzMDU2ZjE3NGFiYw%3D%3D& code_challenge_method=S256& scope=email%2Cid& response_type=code& client_id=984a644ec3b56d32b0404777e1eb73390c

com.mail.cloud.app://oаuth? code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4& state=927489cb2fcdb32e302713f6a720397868b71dd2128c734181983f367d622c24

access_token

https://o2.mail.ru/token? code_verifier=e61748f28b7d5daf881d571df434ed815a4a65b62c0f90bc77b8a3056f174abc& code=b57b236c9bcd2a61fcd627b69ae2d7a6eb5bc13f2dc25311348ee08df43bc0c4& client_id=984a644ec3b56d32b0404777e1eb73390c

access_token

Android IPC

An app that opens IPC channel can confirm authenticity of an app it’s opening by its certificate. The reverse is also true: the opened app can confirm authenticity of the app that opened it.

If a sender sends a request via IPC channel, it can receive an answer via the same channel. Together with the cross-check (item 1), it means that no foreign process can intercept access_token .



code_challenge

state

SDK for clients

Conclusions

Strong foundation is crucial. In case of mobile OAuth 2.0, the foundation is a scheme or a protocol picked out for implementation. It’s easy to make mistakes whilst implementing your own OAuth 2.0 scheme. Others have already taken knocks and learned their lesson; there’s nothing wrong with learning from their mistakes and make secure implementation in one go. The most secure mobile OAuth 2.0 scheme is described in How to do it securely?

Access_token and other sensitive data must be stored in Keychain for iOS and in Internal Storage for Android. These storages were specifically developed just for that. Content Provider can be used in Android, but it must be securely configured.

Client_secret is useless, unless it’s stored in backend. Do not give it away to the public clients.

Do not use WebView for consent screen; use Browser Custom Tab.

To defend against code interception attack, use code_challenge .

To defend against OAuth 2.0 CSRF, use state .

Use HTTPS everywhere, with downgrade forbidden to HTTP. Here is 3-minute demo explaining why (with example from a bug bounty).

Follow the cryptography standards (choice of algorithm, length of tokens, etc). You may copy the data and figure out why it’s done this way, but do not roll your own crypto.

Code must be used only once, with a short lifespan.

From an app client side, check what you open for OAuth 2,0; and from an app provider side, check who opens you for OAuth 2.0.

Keep in mind common OAuth 2.0 vulnerabilities. Mobile OAuth 2.0 enlarges and completes the original one, therefore, redirect_uri check for an exact match and other recommendations for the original OAuth 2,0 are still in force.

You should provide your clients with SDK. They’ll have fewer bugs and vulnerabilities and it’ll be easier for them to implement your OAuth 2.0.



Further reading

«Vulnerabilities of mobile OAuth 2.0» https://www.youtube.com/watch?v=vjCF_O6aZIg

OAuth 2.0 race condition research https://hackerone.com/reports/55140

Almost everything about OAuth 2.0 in one place https://oauth.net/2/

Why OAuth API Keys and Secrets Aren't Safe in Mobile Apps https://developer.okta.com/blog/2019/01/22/oauth-api-keys-arent-safe-in-mobile-apps

[RFC] OAuth 2.0 for Native Apps https://tools.ietf.org/html/rfc8252

[RFC] Proof Key for Code Exchange by OAuth Public Clients https://tools.ietf.org/html/rfc7636

[RFC] OAuth 2.0 Threat Model and Security Considerations https://tools.ietf.org/html/rfc6819

[RFC] OAuth 2.0 Dynamic Client Registration Protocol https://tools.ietf.org/html/rfc7591

Google OAuth 2.0 for Mobile & Desktop Apps https://developers.google.com/identity/protocols/OAuth2InstalledApp



Credits