A JWT profile for OAuth 2 Access Tokens

















- Slides: 17
A JWT profile for OAuth 2 Access Tokens vittorio@auth 0. com @vibronet
TL; DR • OAuth 2 doesn’t mandate a format for access tokens • Many AS products are issuing access tokens in JWT format… • …but everyone does it slightly differently • A JWT profile for ATs would • make it possible to create vendor-agnostic SDKs for API • make it easier for developers to move their code across vendors • Aspects to specify • • Mandatory claims for validation Metadata docs for validation info Claims carrying authorization information Optional claims in common use
Agenda • Analysis of JWT use in ATs issued by production services today • Draft proposal of a JWT as AT profile
ATs as JWTs in the wild
Why are providers using JWT for ATs • Format based validation is a well proven approach • Performant • No extra network traffic • No bottlenecks • Resilient • No throttling • Not sensitive to network issues • Not sensitive to AS outages • Easy troubleshooting • Easy setup/ubiquitous validation SDKs • Easy extensibility (custom claims etc)
Products considered • Auth 0 • Azure AD • Ping Identity • Identity. Server • OKTA • AWS Thanks to Daniel Dobalian, Brian Campbell, Dominick Bauer, Karl Guinness for providing sample ATs in JWT format
Common traits • Nearly everyone use id_token/introspection infrastructural claims • Nearly everyone use OIDC discover to advertise issuer, signing keys • Different claim types for scopes • Wide gamut of additional identity, client & auth info, authorization claims
Claims idtoken Auth 0 Azure AD Ping. Identity. Server AWS OKTA Profile Validation iss aud exp iat nonce auth_time iss aud exp iat nbf iss exp jti [aud] iss aud nbf exp auth_time iss iat exp auth_time iss aud iad exp iss aud exp iat jti auth_time Identity sub lots sub <any> sub name preferred_user name oid ipaddr unique_name sub email uid [sub] sub username sub cid uid sub Authorization N/A scope roles scp groups scope member. Of scope scp scope ? roles, groups Context/misc azp acr amr azp gty aio app_displayname idpid client_id idp amr token_use ver client_id acr amr ? idp ? azpacr appid idp tid uti ver xms_tcdt --- azpacr
A profile for ATs as JWT
ATs as JWT Proposal - summary • ATs are JWTs signed with RS 256 (or any other asymm algo) • Strongly typed to ensure they are not interchangeable w it_tokens • typ=access_token+jwt • Validation coordinates (issuer, signature check keys…) published via • OIDC discovery and/or • AS metadata 8414 • (more or less) same validation rules as id_token in OIDC Core • But ensuring strong type check, disallowing nonce, etc • Mandatory + optional claims layout Thanks to Brian Campbell, Filip Skokan for early feedback and valuable insights
ATs as JWT – claims layout Functional area claim type origin Validation iss as for id_token in OIDC core, introspection exp, iat auth_time Identity Authorization Context/misc aud resource indicators jti 7519 JWT sub 7519 JWT <oidc profile claims> as for id_token in OIDC core scope from token_exchange groups, roles, entitlements 7643 (SCIM) client_id from token_exchange acr, amr as for id_token in OIDC core azpacr <new? > idp <new? > OIDC federation? Bold == mandatory
Discussion
Auth 0 { } { "iss": "https: //flosser. auth 0. com/", "sub": "u. Nk. UAIDPx 1 zg. Xfuodm. R 7 CNHut. YWPZ 96 L@clients", "aud": "https: //flosser. auth 0. com/api/v 2/", "iat": 1537798395, "exp": 1537884795, "azp": "u. Nk. UAIDPx 1 zg. Xfuodm. R 7 CNHut. YWPZ 96 L", "scope": "read: users", "gty": "client-credentials" Validation Identity Authorization Context "iss": "https: //flosser. auth 0. com/", "sub": "auth 0|5 ba 552 d 674717 b 20 e 52 f 56 cd", "aud": [ "https: //flosser. com/api/", "https: //flosser. auth 0. com/userinfo" ], "iat": 1544558774, "exp": 1544645174, "azp": "x. HMI 55 zgw. Y 0 Pnaztf. SQflb. FAwxx. HUM 8_", "scope": "openid profile email read: reports read: appointments offline_access" }
Azure AD { { "aud": "6 e 74172 b-be 56 -4843 -9 ff 4 -e 66 a 39 bb 12 e 3", "aud": "https: //graph. microsoft. com", "iss": "https: //sts. windows. net/26039 cce-489 d-4002 -8293 - "iss": "https: //login. microsoftonline. com/72 f 988 bf-86 f 141 af-91 ab-2 d 7 cd 011 db 47/v 2. 0", 5 b 0 c 5134 eacb/", "iat": 1537231048, "iat": 1551922140, "nbf": 1537231048, "nbf": 1551922140, "exp": 1537234948, "exp": 1551965640, "aio": "42 Jg. YNilx. TPr/L 1 Hx. Vu+Zv. T�nm. ZWBQA=", "AXQAi/8 IAAAAt. Aa. ZLo 3 Ch. Mif 6 KOntt. RB 7 e. Bq 4/Dcc. Qzjc. JGx. PYy/C 3 j. Da. NG "app_displayname": "Test. Sec. Vuln", x. Xd 6 w. NIIVGRgh. NRnw. J 1 l. Oc. An. NZcjvkoyr. Fx. Cttv 33140 Rio. OFJ 4 b. CCGVuo. Ca "appid": "50 ddfc 06 -811 f-4 fcf-85 c 9 -e 7 febdfd 7885", g 1 u. OTT 22222 g. Hw. LPYQ/uf 79 QX+0 KIijdrmp 69 Rctzm. Q==", "appidacr": "1", "idp": "https: //sts. windows. net/26039 cce-489 d-4002 -8293 - "azp": "6 e 74172 b-be 56 -4843 -9 ff 4 -e 66 a 39 bb 12 e 3", "azpacr": "0", 5 b 0 c 5134 eacb/", "name": "Abe Lincoln", "oid": "83820349 -ca 67 -44 e 5 -851 b-79685 d 996 ba 2", "oid": "690222 be-ff 1 a-4 d 56 -abd 1 -7 e 4 f 7 d 38 e 474", "roles": [ "preferred_username": "abeli@microsoft. com", "User. Read. All" "rh": "I", ], "scp": "access_as_user", "sub": "83820349 -ca 67 -44 e 5 -851 b-79685 d 996 ba 2", "sub": "HKZpfa. Hy. Wade. Oou. Ylitjr. I-Kff. Tm 222 X 5 rr. V 3 x. Dqf. KQ", "tid": "26039 cce-489 d-4002 -8293 -5 b 0 c 5134 eacb", "tid": "72 f 988 bf-86 f 1 -41 af-91 ab-2 d 7 cd 011 db 47", "uti": "XHWCc. Mt. Ge. E-u_E-Dv 1 IOAA", "uti": "fqi. Bq. XLPj 0 e. Qa 82 S-IYFAA", "ver": "1. 0", "ver": "2. 0" "xms_tcdt": 1467231125 } }
Ping. Identity { } "scope": "sure: whatever ok: fine", "client_id": "bdc", "iss": "https: //pfdev. ping-eng. com", "aud": "urn: some: api", "sub": "test", "uid": "2 d 425 f 77", "rtttl": 2147483647, "email": "test@example. com", "exp": 1551382159 { "sub": "mdorey+adminaudit@pingidentity. com", "idpid": "24 ad 9 bc 6 -a 69 f-4498 -a 9 be-258126 beaa 6 f", "scope": "openid", "iss": "https: //test-sso. connect. pingidentity. com/cdd 237 bb 3404 -4 ad 4 -90 eb-d 2 e 252808037", "member. Of": [ "Domain Administrators@directory", "Users@directory", "PINGONE. CLOUD. DIRECTORY. GROUP. UI. ENTITLEMENT. OKR GSKYHXRGFKJGBNOGIKRSAZZQJNYFCMKHFULMJPTFRBI" ], "exp": 1533832421, "jti": "IDa 57 ef 23 fb 8909 d 45100882 a 89 d 29 d 56 cfdf 981 fc 6 d 7 fe 2 c 6020 00001651 f 895 ea 0", "client_id": "cdd 237 bb-3404 -4 ad 4 -90 eb-d 2 e 252808037" }
{ Identity. Server { } "nbf": 1551775833, "exp": 1551779433, "iss": "http: //localhost: 5000", "aud": [ "http: //localhost: 5000/resources", "api 1" ], "client_id": "client", "scope": [ "api 1" ] } "nbf": 1551775904, "exp": 1551779504, "iss": "http: //localhost: 5000", "aud": [ "http: //localhost: 5000/resources", "api 1" ], "client_id": "mvc. hybrid", "sub": "88421113", "auth_time": 1551775899, "idp": "local", "scope": [ "openid", "profile", "email", "api 1", "offline_access" ], "amr": [ "pwd" ]
OKTA { "ver": 1, "jti": "AT. 0 m. P 4 JKAZX 1 i. ACIT 4 vb. EDF 7 Lpv. DVjxyp. PMf 0 D 7 u. X 39 RE", "iss": "https: //okta. com/oauth 2/0 oacqf 8 qa. Jw 56 cz. Ji 0 g 4", "aud": "https: //api. example. com", "sub": "00 ujmk. Lgagxe. Rr. Ag 20 g 3", "iat": 1467145094, "exp": 1467148694, "cid": "nmd. P 1 fcyvd. VO 11 AL 7 ECm", "uid": "00 ujmk. Lgagxe. Rr. Ag 20 g 3", "scp": [ "openid", "email", "flights", "custom" ], "custom_claim": "Custom. Value" }