Slide 1

Slide 1 text

Building JavaScript and mobile/native Clients for Token-based Architectures [email protected] @brocklallen & @leastprivilege https://identityserver.io Brock Allen & Dominick Baier

Slide 2

Slide 2 text

2 @leastprivilege / @brocklallen The big Picture Browser Native App Server App "Thing" Web App Web API Web API Web API Security Token Service

Slide 3

Slide 3 text

3 @leastprivilege / @brocklallen Security Protocols (I) Browser Native App Server App "Thing" Web App Web API Web API Web API OpenID Connect* Security Token Service * *

Slide 4

Slide 4 text

4 @leastprivilege / @brocklallen Security Protocols (II) Browser Native App Server App "Thing" Web App Web API Web API Web API OAuth 2.0 OAuth 2.0 OAuth 2.0 OAuth 2.0 OAuth 2.0 OAuth 2.0 Security Token Service * * OpenID Connect*

Slide 5

Slide 5 text

5 @leastprivilege / @brocklallen Clients Browser Native App Web API OAuth 2.0 OAuth 2.0 Security Token Service OpenID Connect OpenID Connect

Slide 6

Slide 6 text

6 @leastprivilege / @brocklallen Token-based Clients… Clients Web API Security Token Service Users (1) Authenticate, establish session (2) identity token, access token (3) process response, validate identity token (4) use access token

Slide 7

Slide 7 text

7 @leastprivilege / @brocklallen Local Logon UI vs Redirect/Server-rendered Username Password Login

Slide 8

Slide 8 text

8 @leastprivilege / @brocklallen Modern/Pure JavaScript apps • Client – Browser-based – Entirely JavaScript (SPA) – Dynamic rendering all client side • Sever • Thin server • Static content (HTML, JS, CSS, etc.) • Ajax endpoints (HTTP APIs)

Slide 9

Slide 9 text

9 @leastprivilege / @brocklallen No more cookies for security • Cookies are the typical approach for server-side applications – But not appropriate for modern JavaScript apps • Modern apps don't have/use server-side HTML framework – SPAs (or mobile apps) are doing the UI client-side • APIs can't use cookies – API might be cross-domain – Cookies don't make sense for non-browser clients – Cross-site request forgery (XSRF) security issues

Slide 10

Slide 10 text

10 @leastprivilege / @brocklallen Discovery https://authority { "issuer": "https://authority", "jwks_uri": "https://authority/.well-known/jwks", "authorization_endpoint": "https://authority/authorize", "token_endpoint": "https://authority/token", "userinfo_endpoint": "https://authority/userinfo", "frontchannel_logout_supported": true, "scopes_supported": [ "openid", "profile", "email", "address", "phone", "offline_access", "api" ], "grant_types_supported": [ "authorization_code", "client_credentials", "password", "refresh_token", "implicit" ], } /.well-known/openid-configuration

Slide 11

Slide 11 text

11 @leastprivilege / @brocklallen Authentication in JS-based apps • OpenId Provider (OP) – Issues tokens • 1) Client makes request to OP – User authenticates – User consents (optional) • 2) OP returns to client – Accept id token – Client validates id token bob secret id_token

Slide 12

Slide 12 text

12 @leastprivilege / @brocklallen Id tokens • Format is JSON web token (JWT) eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMD.4MTkzODAsDQogImh0dHA6Ly9leGFt Header Claims Signature { "typ": "JWT", "alg": "RS256", "kid": "mj399j…" } { "iss": "https://idsrv3", "exp": 1340819380, "aud": "app1", "nonce": "289347898934823", "sub": "182jmm199", "email": "[email protected]", "email_verified": true, "amr": "password", "auth_time": 12340819300 }

Slide 13

Slide 13 text

13 @leastprivilege / @brocklallen Validating id tokens • Steps to validate: 1. Verify state is same as sent in request (prevents XSRF/replay) 2. Base64Url decode id_token and parse into JSON (formatting step) 3. Verify nonce is same as sent in request (prevents XSRF/replay) 4. Validate signature on token (establishes trust [requires crypto]) 5. Validate iss same as issuer of OIDC OP (establishes trust) 6. Validate aud same as this client's identifier (prevents privilege escalation) 7. Validate exp is still valid (prevents stale tokens)

Slide 14

Slide 14 text

14 @leastprivilege / @brocklallen OidcClient • JavaScript helper class that implements OIDC protocol – Includes id_token validation • Including crypto implementation – Heavy use of promises • http://github.com/IdentityModel/oidc-client-js – Also available via npm

Slide 15

Slide 15 text

15 @leastprivilege / @brocklallen More identity data • Might need more than sub (subject) claim • scope used to ask for more identity data

Slide 16

Slide 16 text

16 @leastprivilege / @brocklallen More identity data with user profile • Id token might become too large – Needs to fit into URL • OIDC defines user info endpoint – Ajax call to load user profile – Requires authorization with an access token obtained in OIDC request

Slide 17

Slide 17 text

17 @leastprivilege / @brocklallen Requesting access token • Add "token" to response_type parameter to authorization endpoint • More validation required (same as before, plus): – Hash access token and compare left half to at_hash in id token (ensures id token is paired with access token) id_token access_token access_token user profile id_token token

Slide 18

Slide 18 text

18 @leastprivilege / @brocklallen Using access token to call user profile • Access token passed as Authorization HTTP request header • Response is JSON of user profile based upon requested scopes var xhr = new XMLHttpRequest(); xhr.onload = function () { var user_profile = JSON.parse(xhr.response); } xhr.open("GET", user_profile_endpoint); xhr.setRequestHeader("Authorization", "Bearer " + access_token); xhr.send();

Slide 19

Slide 19 text

19 @leastprivilege / @brocklallen Calling other web APIs • APIs use access token from same OIDC OP • Just need to request more scopes access_token access_token JSON scope: api1

Slide 20

Slide 20 text

20 @leastprivilege / @brocklallen Logout • Throw away tokens in client • Signing out of OIDC OP – Must make request to OP • Post logout redirect – Must pass redirect URL as post_logout_redirect_uri – Must pass original id token as id_token_hint

Slide 21

Slide 21 text

21 @leastprivilege / @brocklallen Token management • Token storage – localStorage – sessionStorage – indexedDb • Token expiration – Access tokens expire (1h, 10h, 1d, 30d, whatever) – Need a way to manage this lifetime • Wait for 401 from API • Renew prior to expiration

Slide 22

Slide 22 text

22 @leastprivilege / @brocklallen Renewing tokens • Request new token in a hidden iframe – only possible if no user interaction is required JS app iframe /authorize?...&prompt=none

Slide 23

Slide 23 text

23 @leastprivilege / @brocklallen Native Clients • Applications that have access to native platform APIs – desktop or mobile – more options / features • OAuth 2.0 / OpenID Connect for native applications – https://tools.ietf.org/wg/oauth/draft-ietf-oauth-native-apps/

Slide 24

Slide 24 text

24 @leastprivilege / @brocklallen Using a web server for driving the authentication workflow authentication request render UI & workflow

Slide 25

Slide 25 text

25 @leastprivilege / @brocklallen Browser types • Embedded web view – private browser & private cookie container – e.g. WinForms or WPF browser control • Authentication broker – "special" browsers (look private but share some cookies) – e.g. Win8 & UWP WebAuthenticationBroker • In-app browser tab / system browser – full blown system browser (including address bar & add-ins) – shared cookie container – e.g. SafariViewController (iOS9) & Chrome Custom Tabs (Android 5)

Slide 26

Slide 26 text

26 @leastprivilege / @brocklallen OpenID Connect Hybrid Flow GET /authorize ?client_id=nativeapp &scope=openid profile api1 api2 offline_access &redirect_uri=com.mycompany.nativeapp://cb &response_type=code id_token &nonce=j1y…a23 &code_challenge=x929..1921 nonce = random_number code_verifier = random_number code_challenge = hash(code_verifier)

Slide 27

Slide 27 text

27 @leastprivilege / @brocklallen Receiving the response GET com.mycompany.nativeapp://cb #id_token=x12f…zsz &code=818…1299 callback

Slide 28

Slide 28 text

28 @leastprivilege / @brocklallen Validating the response • Identity token validation (section 3.1.3.7) – validate signature • key material available via discovery endpoint – validate iss claim – validate exp (and nbf) – validate aud claim • Authorization code validation (section 3.3.2.10) – hash authorization code and compare with c_hash claim https://openid.net/specs/openid-connect-core-1_0.html

Slide 29

Slide 29 text

29 @leastprivilege / @brocklallen Requesting the access token • Exchange code for access token – using client id and secret code & code verifier (client_id) { access_token: "xyz…123", refresh_token: "dxy…103", id_token: "xyz…123", expires_in: 3600, token_type: "Bearer" }

Slide 30

Slide 30 text

30 @leastprivilege / @brocklallen Next steps • Persist the data in protected storage – claims – access token – refresh token • Use access token to communicate with APIs • Use refresh token to get new access tokens when necessary

Slide 31

Slide 31 text

31 @leastprivilege / @brocklallen Refreshing an Access Token refresh_token (client_id) { access_token: "xyz…123", refresh_token: "jdj9…192j", expires_in: 3600, token_type: "Bearer" }

Slide 32

Slide 32 text

32 @leastprivilege / @brocklallen Libaries • C# portable class library – desktop .NET, UWP, Xamarin iOS & Android • https://github.com/IdentityModel/IdentityModel.OidcClient • C# NetStandard 1.4 library (WIP) – desktop .NET, .NET Core, Xamarin iOS & Android • Native libraries – https://github.com/openid/AppAuth-iOS – https://github.com/openid/AppAuth-Android

Slide 33

Slide 33 text

33 @leastprivilege / @brocklallen thank you! get slides from https://speakerdeck.com/leastprivilege