OAUTH 2.0 AND OPENID CONNECT FOR SINGLE PAGE APPLICATIONS

DR. PHILIPPE DE RYCK

https://Pragmatic Web Security.com A RESTAURANT REVIEW APPLICATION

FRONTEND AND MOBILE APPLICATIONS

APIS TO EMPOWER PARTNER SITES

@PhilippeDeRyck OpenID Connect OAuth 2.0 Authenticate the user for me? Can I access the API please? Help me out here, is this client allowed to do that? OAuth 2.0

Can you handle this for me please? Here's an access token

OAuth 2.0 @PhilippeDeRyck TERMINOLOGY

This session OAuth 2.0 OpenID Connect

User Resource Owner End-User

API Resource Server

Security Token Service (STS) Server OpenID Provider

Client Client Relying Party

@PhilippeDeRyck I am Dr. Philippe De Ryck

Founder of Pragmatic Web Security

Google Developer Expert

Auth0 Ambassador / Expert

SecAppDev organizer

I help developers with security

Academic-level security training

Hands-on in-depth online courses

Security advisory services https://pragmaticwebsecurity.com THE IMPLICIT FLOW

5 Allow the restograde app access? 3 Who are you? Authenticate please!

4 Authenticate to restograde.com 6 Authorize access

Follow redirect to restograde.com 2

7 Redirect to the restograde app with access token

9 Request with 1 Navigate browser to restograde.com access token

8 Reload application with access token 10 Response THE IMPLICIT FLOW

5 Allow the restograde app access? 3 Who are you? Authenticate please!

4 Authenticate to restograde.com 6 Authorize access

Follow redirect to restograde.com 2 Access tokens are sent in the insecure frontchannel. Refresh tokens are not 7 Redirect to the restograde app with access token allowed in the Implicit flow.

9 Request with 1 Navigate browser to restograde.com access token

8 Reload application with access token 10 Response THE AUTHORIZATION CODE FLOW WITH PKCE

8 Store code challenge 6 Allow the restograde frontend access? with the authorization code 4 Who are you? Authenticate please!

5 Authenticate to restograde.com 7 Authorize access 12 Check code verifier against stored code challenge

Exchange Follow redirect to restograde.com Identity token, access token with code challenge 3 authorization code 11 13 with code verifier (& refresh token)

9 Redirect to the restograde frontend with authorization code 14 Use information from identity Generate code verifier and token to "authenticate" the user 1 calculate code challenge 15 Request with 2 Open restograde.com with code challenge access token

10 Follow redirect with authorization code 16 Response The frontend generates and stores the code verifier in the browser.

The code challenge illustrates that the frontend knew this secret in step 2, and still knows it in step 11 No tokens are issued through the URL-based redirect mechanism.

The authorization code has a limited lifetime, can only be used once, and only in combination with the code verifier The Authorization Code flow also supports refresh tokens 2 3 The redirect URI

1 https://sts.restograde.com/authorize 2 ?response_type=code Indicates the authorization code flow 3 &client_id=lY5g0BKB7Mow4yDlb6rdGPsO2i1g7Osv The client requesting access 4 &scope=read 5 &redirect_uri=https://web.restograde.com/callback Where the STS should send the code 6 &state=s0wzojm2w8c23xzprkk6 7 &code_challenge=JhEN0Amnj7B…Wh5PxWitZYK1woWh5PxWitZY The PKCE code challenge 8 &code_challenge_method=S256

@PhilippeDeRyck 11 The request to exchange the authorization code

1 POST https://sts.restograde.com/oauth/token 2 3 grant_type=authorization_code Indicates the code exchange request 4 &client_id=lY5g0BKB7Mow4yDlb6rdGPsO2i1g7Osv The client exchanging the code 5 &redirect_uri=https://web.restograde.com/callback The redirect URI used before 7 &code=SplxlOBeZQQYbYS6WxSbIA The code received in step 10 8 &code_verifier=lT5q6nbPQRtdj…~IUdkErVDFG.fF4z7CzCxo The code verifier from step 1

@PhilippeDeRyck https://flowsimulator.pragmaticwebsecurity.com Loading the Auth0 service in an Angular application

1 @NgModule({ 2 imports: [ 3 BrowserModule, 4 AuthModule.forRoot({ Configure the SDK with the domain 5 domain: 'sts.restograde.com', of your tenant and the clientID of 6 clientId: 'lY5g0BKB7Mow4yDlb6rdGPsO2i1g7Osv', the SPA application 7 }), 8 ], 9 … 10 })

Service methods for relevant OAuth 2.0 / OIDC features

1 constructor(public auth: AuthService) {} 2 3 login() { The Auth0 AuthService exposes all relevant features to use in 4 this.auth.loginWithRedirect(); components and services 5 } 6 logout() { 7 this.auth.logout({ returnTo: window.location.origin }); 8 } @PhilippeDeRyck Wrapping the Auth0 React SDK around the application

1 ReactDOM.render( 2 7 8 , 9 document.getElementById('app') 10 ); Hooks for relevant OAuth 2.0 / OIDC features

1 const { 2 isLoading, 3 isAuthenticated, Feature-specific hooks expose all 4 error, relevant information and 5 user, operations for use in components 6 loginWithRedirect, 7 logout, 8 getAccessTokenSilently, 9 } = useAuth0(); @PhilippeDeRyck Auth0's generic JS SDK, which can be used in any JS-based framework or frontend application

@PhilippeDeRyck The Implicit flow is deprecated in favor of the Authorization Code flow with PKCE

The Implicit flow has not become more vulnerable, the other flow is just better

@PhilippeDeRyck ? What if an access token expires? THE REFRESH TOKEN FLOW

Request new access token 2 3 Access token & refresh token with refresh token

4 Request with access token Frontend has an access token and refresh token, and monitors 1 5 Response access token expiration What if an attacker injects malicious code ? to steal the tokens from the SPA? Third-party Remote code dependencies inclusions

External content User-provided (e.g., ads) data

@PhilippeDeRyck REFRESH TOKEN ROTATION

• Refresh token rotation is required for using refresh tokens in the browser • Part of the OAuth 2.0 for Browser-Based Apps proposal & OAuth 2.1 draft • Refresh tokens are used once to obtain a new access token and new refresh token • Previously used refresh tokens become invalid

App obtains tokens App refreshes tokens App refreshes tokens App refreshes tokens AT1 and RT1 Use RT1 Use RT2 Use RT3 Receive AT2 and RT2 Receive AT3 and RT3 Receive AT4 and RT4

AT1 expires AT2 expires AT3 expires

@PhilippeDeRyck REFRESH TOKEN ROTATION

2 The request to exchange a refresh token

1 POST https://sts.restograde.com/oauth/token 2 3 grant_type=refresh_token 4 &client_id=DtsTliLAWq3JXIwaoPQzl8vXhNI6qGnb 5 &refresh_token=8xLOxBtZp8 The latest refresh token obtained from the STS

3 The response from the

1 { 2 "access_token": "eyJhbGciO...du6TY9w", A new access token 3 "token_type": "Bearer", 4 "expires_in": 3600, 5 "refresh_token": "mTVeKoIZYy", A new refresh token to be used in the next flow 6 }

@PhilippeDeRyck DETECTING REFRESH TOKEN ABUSE

• When the STS detects the re-use of a refresh token, something is wrong • The refresh token is immediately revoked, preventing abuse

• To ensure security, the STS revokes the entire token chain of this refresh token • The abuse of RT2 leads to the revocation of RT3, RT4, …

App obtains tokens App refreshes tokens App refreshes tokens AT1 and RT1 Use RT1 Use RT2 Receive AT2 and RT2

AT1 expires Attacker uses RT2 AT2 expires Receive AT3 and RT3 STS notices reuse of RT2 Attacker steals RT2 No tokens are issued @PhilippeDeRyck RT3 is revoked DETECTING REFRESH TOKEN ABUSE

• When the STS detects the re-use of a refresh token, something is wrong • The refresh token is immediately revoked, preventing immediate abuse

• To ensure security, the STS revokes the entire token chain of this refresh token • The abuse of RT2 leads to the revocation of RT3, RT4, …

App obtains tokens App refreshes tokens App refreshes tokens STS notices reuse of RT2 AT1 and RT1 Use RT1 Use RT2 No tokens are issued Receive AT2 and RT2 Receive AT3 and RT3 RT3 is revoked

AT1 expires AT2 expires AT3 expires Attacker uses RT2 Attacker steals RT2 @PhilippeDeRyck ? Problem solved, right? SIDESTEPPING REFRESH TOKEN ROTATION

https://app.restograde.com 1 Monitor the app for refresh tokens (if available) 2 Keep running the refresh flow when needed 3 Return new access tokens and refresh tokens 4 4 Send tokens to a server controlled by the attacker 5 Wait for the app to become inactive to use RT

1

2 The SDK using refresh tokens to renew access tokens 3

5 STEALING ACCESS TOKENS FROM THE LEGITIMATE SDK https://app.restograde.com 1 Run the refresh token flow to get fresh tokens 2 The tokens returned by the STS 3 Extract accessible tokens from the JS environment 4 4 Send access tokens to an attacker-controlled server

3

1 The SDK using refresh tokens to renew access tokens 2 ? So, we're screwed? ! Yes. THE CONCEPT OF A BACKEND-FOR-FRONTEND

The Restograde application Run the Authorization Code flow with client 1

2 Issue access token and refresh token

Proxy API requests with access token Cookie-based session 3 retrieved from session

The OAuth 2.0 client application

The client can follow best practices for backend applications (client authentication, sender constrained tokens, …) THE DETAILS OF A BACKEND-FOR-FRONTEND

5 Authentication / client authorization

Follow redirect to restograde.com 4

1 Login to Restograde Exchange authorization code 8 9 Identity token, access token, & refresh token with client authentication

6 Redirect to BFF with code

Use information from identity 3 Initialize the code flow 10 token to "authenticate" the user

7 Redirect with authorization code Lookup tokens with session 13 14 Request with 2 Login 12 API request access token

11 Logged in 16 API data 15 Response Sensitive Single Page Applications should ! definitely consider using a BFF KEY TAKEAWAYS

1 Use the Authorization Code flow with PKCE over the Implicit flow

2 Use short access tokens lifetimes and refresh tokens with rotation

3 Sensitive SPAs should avoid tokens in the browser in favor of a BFF

@PhilippeDeRyck This online course condenses dozens of confusing specs into a crystal-clear academic-level learning experience

25% discount

Use coupon code

DEV_DAY2020

Offer expires Nov. 25th, 2020

https://courses.pragmaticwebsecurity.com Thank you for watching! Connect on social media for more in-depth security content

@PhilippeDeRyck /in/PhilippeDeRyck