Slide 1

Slide 1 text

OAuth in 2021 What’s new? Dominick Baier @leastprivilege

Slide 2

Slide 2 text

2 @leastprivilege Me • Co-Creator of IdentityServer & IdentityModel OSS Project – Certified OpenID Connect & OAuth 2.0 Engine for ASP.NET Core – https://identityserver.io • Co-Creator of PolicyServer – Authorization for modern Applications – https://policyserver.io • Co-Founder of Duende Software – the new home of IdentityServer – https://duendesoftware.com email [email protected] blog https://leastprivilege.com twitter @leastprivilege slides https://speakerdeck.com/leastprivilege

Slide 3

Slide 3 text

3 @leastprivilege IdentityServer https://github.com/DuendeSoftware/IdentityServer https://github.com/IdentityServer/IdentityServer4

Slide 4

Slide 4 text

4 @leastprivilege Agenda • OAuth 2.1 • Metadata, signing algorithms & key rotation • JWT profile for OAuth access tokens • Resource Isolation • JAR & PAR • Proof-of-possession access tokens https://duendesoftware.com/training/highsecurityoauth

Slide 5

Slide 5 text

5 @leastprivilege OAuth 2.1

Slide 6

Slide 6 text

6 @leastprivilege Current BCPs & Security Considerations OAuth 2.0 Security Best Current Practice https://tools.ietf.org/html/draft-ietf-oauth- security-topics/ OAuth 2.0 Threat Model & Security Considerations https://tools.ietf.org/html/rfc6819 JSON Web Token Best Current Practices https://tools.ietf.org/html/rfc8725 OAuth 2.0 for native Applications https://tools.ietf.org/html/rfc8252 OAuth 2.0 for Browser-Based Applications https://tools.ietf.org/wg/oauth/draft-ietf-oauth-browser-based-apps/ JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens https://tools.ietf.org/wg/oauth/draft-ietf-oauth- access-token-jwt/

Slide 7

Slide 7 text

7 @leastprivilege No more Implicit Flow • Implicit flow was a workaround in 2012 to overcome cross-origin AJAX limitations – solved now with CORS • Error prone – tokens are transmitted via URLs and can leak easily • log files • browser history • referrer headers • implementation errors • proxies – no solution for token injection attacks (without adding OpenID Connect)

Slide 8

Slide 8 text

8 @leastprivilege Implicit Flow Request GET /authorize ?client_id=app1 &redirect_uri=https://app.com/cb.html &response_type=token &state=j1y…a23 &scope=api1 api2

Slide 9

Slide 9 text

9 @leastprivilege Implicit Flow Response GET /callback.html #token=32x…133 &expires_in=3600 &token_type=Bearer &state=j1y…a23

Slide 10

Slide 10 text

10 @leastprivilege No more Password Grant • Exposes user's credentials to client – increased attack surface – credentials can leak in more places than just the authorization server – users are trained to type in credentials in multiple places • more prone to phishing attacks • Incompatible with modern authentication flows – FIDO – 2FA – federation

Slide 11

Slide 11 text

11 @leastprivilege Original Flows Application Type Flow Machine to Machine Client Credentials Flow Server-side Web Application OAuth Code Flow OpenID Connect Hybrid Flow Password Grant Native Desktop/Mobile Applications Implicit Flow OAuth Code Flow OpenID Connect Hybrid Flow Password Grant SPAs Implicit Flow Password Grant Code Flow

Slide 12

Slide 12 text

12 @leastprivilege Grand Unification Application Type Flow Machine to Machine Client Credentials Flow Interactive Application Code Flow + PKCE

Slide 13

Slide 13 text

13 @leastprivilege Authorization Code Injection GET /authorize ?client_id=app1 &redirect_uri=https://app.com/cb GET /cb?code=stolen client id/secret/stolen access token 3 1 2

Slide 14

Slide 14 text

14 @leastprivilege Mitigation: Proof Key for Code Exchange GET /authorize ?client_id=app1 &redirect_uri=https://app.com/cb &code_challenge=cy..fc GET /cb?code=xyz client id/secret code / code_verifier access token 3 1 2 code_verifier = random number code_challenge = hash(code_verifier)

Slide 15

Slide 15 text

15 @leastprivilege Redirect URI Validation Attacks • Problematic – https://*.somesite.com/cb • e.g. sub-domain takeover – https://somesite.com/* • find a page with an open redirector • find a page where attacker can inject content GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13 &redirect_uri=https://somesite.com/some_endoint

Slide 16

Slide 16 text

16 @leastprivilege OAuth 2.1 • RFC 6749 + RFC 6750 + various BCPs == OAuth 2.1 – https://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2-1/ • Most important changes – omission of implicit grant – omission of password grant – PKCE must be used with the authorization code grant – redirect URIs must be compared using exact string matching – refresh tokens must be either sender-constrained or one-time use only – bearer tokens usage omits token in the query string

Slide 17

Slide 17 text

17 @leastprivilege OAuth Metadata, Key Rotation & Signing Algorithms

Slide 18

Slide 18 text

18 @leastprivilege OAuth/OpenID Connect Metadata

Slide 19

Slide 19 text

19 @leastprivilege Key Rotation & Algorithms • Rotation – recommended to rotate keys regularly – must keep a history of old keys (depending on max access token lifetime) • Algorithms – OIDC mandates RS256 as a minimum – OpenBanking/PSD2 requires PS256 – FHIR requires EC-based algorithms

Slide 20

Slide 20 text

20 @leastprivilege JWT Profile for OAuth Access Tokens

Slide 21

Slide 21 text

21 @leastprivilege Access Tokens • RFC 6749 has no information about token format or content

Slide 22

Slide 22 text

22 @leastprivilege Self-contained Tokens • JWTs are convenient to use and have good library support { "typ": "JWT", "alg": "RS256", "kid": "mj399j…" } { "iss": "https://issuer", "exp": 1340819380, "nbf": 1340818761, "client_id": "client", "user_id": "bob", "scope": "scopeA scopeB", } Header Payload eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMD.4MTkzODAsDQogImh0dHA6Ly9leGFt Header Payload Signature

Slide 23

Slide 23 text

23 @leastprivilege JWT Claim Types (all optional) • iss (Issuer) – identifies the principal that issued the JWT • sub (Subject) – identifies the principal that is the subject of the JWT • aud (Audience) – identifies the recipients that the JWT is intended for • exp (Expiration Time) – identifies the expiration time on or after which the JWT MUST NOT be accepted for processing • nbf (Not Before) – identifies the time before which the JWT MUST NOT be accepted for processing • iat (Issued At) – identifies the time at which the JWT was issued • jti (JWT ID) – provides a unique identifier for the JWT https://tools.ietf.org/html/rfc7519#section-4.1

Slide 24

Slide 24 text

24 @leastprivilege Lessons learned with JWTs • In absence of a standard, every vendor came up with different claims and semantics – how to convey user and client ID – define additonal claims – nbf vs iat – scopes vs audiences – required vs optional – cross-JWT confusion and substitution attacks • Two specs aim to provide interoperable definitions – https://tools.ietf.org/html/rfc8725 – https://datatracker.ietf.org/doc/html/draft-ietf-oauth-access-token-jwt

Slide 25

Slide 25 text

25 @leastprivilege sub & client_id • client_id – OAuth client ID • sub – if user is present: sub as defined in OpenID Connect – if no user is present: OAuth client ID https://datatracker.ietf.org/doc/html/draft-ietf-oauth-access-token-jwt#section-2.2

Slide 26

Slide 26 text

26 @leastprivilege Other required claims • iss – identifies the principal that issued the JWT • exp – identifies the expiration time on or after which the JWT MUST NOT be accepted for processing • aud – identifies the recipient(s) that the JWT is intended for • iat – identifies the time at which the JWT was issued • jti – provides a unique identifier for the JWT

Slide 27

Slide 27 text

27 @leastprivilege Scopes in JWTs { "iss": "issuer", "aud": "audience", "exp": 1340819380, "iat": 1340818761, "jti": "random_id" "sub": "123", "client_id": "456", "scope": "scopeA scopeB" }

Slide 28

Slide 28 text

28 @leastprivilege Cross-JWT Confusion https://www.rfc-editor.org/rfc/rfc8725.html#name-cross-jwt-confusion

Slide 29

Slide 29 text

29 @leastprivilege Example JWT per Profile { "typ": "at+jwt", "alg": "RS256", "kid": "mj399j…" } { "iss": "issuer", "aud": "audience", "exp": 1340819380, "iat": 1340818761, "jti": "random_id" "sub": "123", "client_id": "456", "scope": "scopeA scopeB" }

Slide 30

Slide 30 text

30 @leastprivilege Resource Isolation

Slide 31

Slide 31 text

31 @leastprivilege Resource Isolation • OAuth uses the scope parameters to indicate "what" the clients wants to access The authorization and token endpoints allow the client to specify the scope of the access request using the "scope" request parameter. The value of the scope parameter is expressed as a list of space-delimited, case- sensitive strings. If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope. The authorization server MAY fully or partially ignore the scope requested by the client, based on the authorization server policy or the resource owner's instructions. RFC6749

Slide 32

Slide 32 text

32 @leastprivilege Example: GitHub Scopes https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/

Slide 33

Slide 33 text

33 @leastprivilege Example: Google Scopes https://developers.google.com/oauthplayground/

Slide 34

Slide 34 text

34 @leastprivilege Problems with Scope Model • Token can be re-used at multiple resources – makes tokens very powerful • in case of leakage – prohibits resource-specific processing • e.g. token content, encryption – concerns about token re-use • especially with absence of sender constraints • Can't prohibit that clients requests token that can be used at multiple resources

Slide 35

Slide 35 text

35 @leastprivilege Shared vs Resource-specific Access Tokens { "iss": "https://issuer", "iat": 1340818761, "exp": 1340819380, "client_id": "backoffice", "scope": [ "orders", "invoicing" ] "sub": "182jmm199", "more_data": "…" } orders invoicing trust boundary?

Slide 36

Slide 36 text

36 @leastprivilege Resource Indicators • RFC 8707 introduces new request parameter – resource • Indicates target service or resource to which access is being requested – must be an absolute URI – might be a physical network address – multiple resource parameters are allowed

Slide 37

Slide 37 text

37 @leastprivilege Authorize Request • Request access to one or more resource – can be combined with scope GET /as/authorize? client_id=client& response_type=code& state=xyz& redirect_uri=https://client/cb& resource=https://invoice.api.com& resource=https://customer.api.com 1

Slide 38

Slide 38 text

38 @leastprivilege Redeeming the Code • Resource for access token must be specified – returned token is for the requested resource only POST /as/token grant_type=authorization_code& redirect_uri=https//client/cb& code=abc& resource=https://invoice.api.com { "access_token":"ey…Yw", "token_type":"Bearer", "expires_in":3600, "refresh_token":"4L…C2" } 2 { "iss": "http://as.com", "aud": "https://invoice.api.com" "iat": 1588420700 "exp": 1588420800, "sub": "123", "client_id": "456" }

Slide 39

Slide 39 text

39 @leastprivilege Getting access to other Resources • Refresh token can be used to request additional access tokens POST /as/token grant_type=refresh_token& refresh_token=4L…C2& resource=https://customer.api.com { "access_token":"ey…Yw", "token_type":"Bearer", "expires_in":3600, "refresh_token":"4L…C2" } 3 { "iss": "http://as.com", "aud": "https://customer.api.com" "iat": 1588420700 "exp": 1588420800, "sub": "123", "client_id": "456" }

Slide 40

Slide 40 text

40 @leastprivilege JWT Secured Authorize Request (JAR) Pushed Authorize Request (PAR)

Slide 41

Slide 41 text

41 @leastprivilege JWT Secured Authorization Requests (JAR) • Allows passing authorization parameters as a JSON Web Token – signed for integrity and client authentication – encrypted for confidentiality • Mitigates several known attacks – redirection URI re-writing – mix-up attacks https://tools.ietf.org/html/draft-ietf-oauth-jwsreq

Slide 42

Slide 42 text

42 @leastprivilege JWT Secured Authorization Request GET /authorize?client_id=client&response_type=code&redirect_uri=https://myapp.com/cb &state=abc&code_challenge=def&scope=openid customer.api GET /authorize?client_id=client&request= eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ewogICAgImlzcyI6ICJzNkJoZF JrcXQzIiwKICAgIC.JhdWQiOiAiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iL Aog ICAgInJlc3BvbnNlX3R5cGUiOiAiY29kZSBpZF90b2tlbiIsCiAgICAiY2xpZW 50X2 lkIjogInM2QmhkUmtxdDMiLAogICAgInJlZGlyZWN0X3VyaSI6ICJodHR wczovL2Ns aWVudC5leGFtcGxlLm9yZy9jYiIsCiAgICAic2NvcGUiOiAib3Blbml kIiwKICAgIC JzdGF0ZSI6ICJhZjBpZmpzbGRraiI.sCiAgICAibm9uY2UiOiAibi0wU zZfV3pBMk1q IiwKICAgICJtYXhfYWdlIjogODY0MDAKfQ.Nsxa_18VUElVaPjqW _ToI1yrEJ67BgK b5xsuZRVqzGkfKrOIX7BCx0biSxYGmjK9KJPctH1OC0iQJwXu5Y

Slide 43

Slide 43 text

43 @leastprivilege Request Object { "typ": "oauth.authz.req+jwt", "alg": "RS256", "kid": "1" }. { "iss": "client", "aud": "https://authorizationserver.com", "response_type": "code", "client_id": "client", "redirect_uri": "https://myapp.com/cb", "scope": "openid customer.api", "state": "abc", "code_challenge": "def" }. [Signature]

Slide 44

Slide 44 text

44 @leastprivilege Pushed Authorization Requests (PAR) • Complements JAR • Provides interoperable endpoint to push authorization request parameters – in exchange for a request_uri – allows for authentication for confidential clients – manages entropy and lifetime https://tools.ietf.org/html/draft-ietf-oauth-par

Slide 45

Slide 45 text

45 @leastprivilege Pushed Authorization Request & Response POST /par client_id=client& response_type=code& redirect_uri=https://myapp.com/cb& state=abc& code_challenge=def& authorization_details={…} HTTP/1.1 201 Created { "request_uri": "urn:par:bwc4JK-cc191e-Y1LTC2", "expires_in": 90 }

Slide 46

Slide 46 text

46 @leastprivilege Authorization Request using request_uri • Front-channel only used to transmit reference to authenticated and validated request object GET /authorize?client_id=client& request_uri=urn:par:bwc4JK-cc191e-Y1LTC2

Slide 47

Slide 47 text

47 @leastprivilege Proof-of-Possession (PoP) Access Tokens

Slide 48

Slide 48 text

48 @leastprivilege Weakness of Bearer Tokens • Bearer tokens are not bound to the client – attacker can replay leaked tokens Authorization: Bearer

Slide 49

Slide 49 text

49 @leastprivilege "Proof-of-Possession" History • Most wanted feature since initial OAuth 2.0 spec – only bearer tokens were specified • Several attempts to introduce PoP at the application layer – required a lot of application layer crypto (e.g. signing of HTTP requests) – some unsolved problems (e.g. streaming) • ...then at the transport layer – HTTP token binding (deprecated)

Slide 50

Slide 50 text

50 @leastprivilege PoP Today • Transport Layer – Mutual TLS – https://tools.ietf.org/html/rfc8705 • Application Layer – DPop – https://tools.ietf.org/html/draft-fett-oauth-dpop

Slide 51

Slide 51 text

51 @leastprivilege cnf Claim { "iss": "https://server.example.com", "client_id": "client1", "exp": 1493726400, "cnf": { "some_key_material" } } HTTP/1.1 200 OK Content-Type: application/json { "active": true, "iss": "https://server.example.com", "client_id": "client1", "exp": 1493726400, "cnf": { "some_key_material" } } JWT Introspection https://tools.ietf.org/html/rfc7800

Slide 52

Slide 52 text

52 @leastprivilege Mutual TLS TLS Tunnel server certificate client certificate • authentication • proving to know a secret (private key) • validate chain of trust (optional) • negotiate key material • sign & encrypt traffic

Slide 53

Slide 53 text

53 @leastprivilege Creating an X.509 Client Certificate static X509Certificate2 CreateClientCertificate(string name) { var distinguishedName = new X500DistinguishedName($"CN={name}"); using (var rsa = RSA.Create(2048)) { var request = new CertificateRequest( distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add( new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); request.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension( new OidCollection { new Oid("1.3.6.1.5.5.7.3.2") }, false)); return request.CreateSelfSigned( new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(10))); } }

Slide 54

Slide 54 text

54 @leastprivilege Setting a Client Certificate static SocketsHttpHandler GetHandler(X509Certificate2 certificate) { var handler = new SocketsHttpHandler(); handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate }; return handler; }

Slide 55

Slide 55 text

55 @leastprivilege Calling the Token Endpoint static async Task RequestTokenAsync() { var client = new HttpClient(GetHandler()); var disco = await client.GetDiscoveryDocumentAsync("https://demo.duendesoftware.com"); var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.MtlsEndpointAliases.TokenEndpoint, ClientId = "mtls", Scope = "api1" }); return response; }

Slide 56

Slide 56 text

56 @leastprivilege Proof-of-Possession with MTLS { "iss": "https://issuer", "exp": 1340819380, "nbf": 1340818761, "client_id": "182jmm199", "scope": "api1", "cnf": { "x5t#S256": "bwcK0esc3ACC3DB2Y5_lESsXE8o9ltc05O89jdN-dg2" } }

Slide 57

Slide 57 text

57 @leastprivilege Verifying Access Token Ownership at Resource public void ConfigureServices(IServiceCollection services) { services.AddAuthentication("jwt") .AddJwtBearer("jwt", options => { … }) .AddCertificate(options => { … }); services.AddCertificateForwarding(…) } public void Configure(IApplicationBuilder app) { app.UseForwardedHeaders(…) app.UseCertificateForwarding(); app.UseAuthentication(); app.UseConfirmationValidation(…) app.UseAuthorization(); }

Slide 58

Slide 58 text

58 @leastprivilege Application Level Proof-of-Possession • OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) An application-level sender-constraining for access and refresh tokens that can be used in cases where MTLS is not available. It uses proof-of- possession based on a public/private key pair and application-level signing. DPoP can be used with public clients and, in case of confidential clients, can be combined with any client authentication method. https://tools.ietf.org/html/draft-fett-oauth-dpop

Slide 59

Slide 59 text

59 @leastprivilege DPop Token Request POST /token grant_type=authorization_code code=xyz redirect_uri=https://… Dpop: eyJ0…3Lg

Slide 60

Slide 60 text

60 @leastprivilege DPop Proof Token (Token Request) { "typ": "dpop+jwt", "alg": "ES256", "jwk": { "kty": "EC", "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", "crv": "P-256" } }. { "jti": "-BwC3ESc6acc2lTc", "htm": "POST", "htu": "https://server.example.com/token", "iat": 1562262616 }.[Signature] public key unique ID HTTP method HTTP URL signature proves knowledge of private key

Slide 61

Slide 61 text

61 @leastprivilege DPop Token Response • Access token contains the public key as confirmation method { "iss": "https://server.example.com", "client_id": "client1", "exp": 1493726400, "cnf": { "jkt":"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I" } } Base64url encoding of the JWK SHA-256 Thumbprint of the public key (RFC 7638) https://tools.ietf.org/html/rfc7638

Slide 62

Slide 62 text

62 @leastprivilege Resource Access GET /resource Authorization: Dpop ey…MQ DPop: ey…bQ

Slide 63

Slide 63 text

63 @leastprivilege DPoP Proof Token (Resource Access) { "typ": "dpop+jwt", "alg": "ES256", "jwk": { "kty": "EC", "x": "l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs", "y": "9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA", "crv": "P-256" } }. { "jti": "e1j3V_bKic8-LAEB", "htm": "GET", "htu": "https://resource.example.org/resource", "iat": 1562262618 }.[Signature] same public key resource must create jkt and compare with access token signature proves knowledge of private key

Slide 64

Slide 64 text

64 @leastprivilege Summary • OAuth keeps evolving • OAuth 2.1 – wraps up several guidance documents – removes protocol flows that turned out to be problematic • Additional specs strengthen OAuth security – JAR & PAR – Resource Indicators – PoP