OpenID Connect is a thin layer on top of OAuth 2.0 that provides authorization (identity) on top of authentication (OAuth 2.0). OIDC introduced the identity token (JWT) which contains info about the user.
Allows users to grant access to resources without sharing the password and with revoke-ability.
NOTE: Flows are called grants
in the specification.
The precursor to Authorization Flow. No longer used because tokens were returned directly to the browser and could be leaked. Designed for a time when JS could not access browser history or local storage.
In addition, most providers did not allow the CORS POST to /token
endpoint
that is needed in the Authorization Code flow.
Sample request to the authorize call to the Auth Server:
https://dev-micah.okta.com/oauth2/default/v1/authorize?
client_id=0oapu4btsL2xI0y8y356
&redirect_uri=http://localhost:8080/callback
&response_type=id_token token // What identifies Implicit Flow (ID Token + Access Token returned).
&response_mode=fragment
&state=SU8nskju26XowSCg3bx2LeZq7MwKcwnQ7h6vQY8twd9QJECHRKs14OwXPdpNBI58
&nonce=Ypo4cVlv0spQN2KTFo3W4cgMIDn6sLcZpInyC40U5ff3iqwUGLpee7D4XcVGCVco
&scope=openid profile email
Sample redirect back from the Auth Server:
http://localhost:8080/authorization-code/callback#
id_token=eyJraWQiOiJUTmJhREFfbDBua3RWRHdaMi12RlNUNENEVTlQM182VnBuZ3FRWmVEM0hvIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHUydmhvM2tmOWR2YnhPaDVkNyIsIm5hbWUiOiJTenltb24gV29qbmFyIiwiZW1haWwiOiJzenltb25yd29qbmFyQGdtYWlsLmNvbSIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtMzk5MDc0MTUub2t0YS5jb20vb2F1dGgyL2RlZmF1bHQiLCJhdWQiOiIwb2EydmxpMzRqUGxDYjVlZTVkNyIsImlhdCI6MTYzNzg2MzQ1OCwiZXhwIjoxNjM3ODY3MDU4LCJqdGkiOiJJRC5kaW44UEJCcjZYN1ZPbmhaSF9DV2hMd3B1SlJHWm1CVVBjNElMS1pDVUI0IiwiYW1yIjpbInB3ZCJdLCJpZHAiOiIwMG8ydmhrcnMzQXVLU29ZNTVkNyIsIm5vbmNlIjoibGtQOGxNeDFuZG5nZE54ZXQ1S1RXbE9kcnFodmhhYmNVMUdmM1J2bHo4TTNhR1JCbEQ4VFI4NEdaM3g1MmFCNSIsInByZWZlcnJlZF91c2VybmFtZSI6Indvam5hcjQ4QGdpdGh1Yi5va3RhaWRwIiwiYXV0aF90aW1lIjoxNjM3ODYzNDU3LCJhdF9oYXNoIjoiS0pJS3g5MjAzbXF2YXFXZC1aZDNoUSJ9.fbAyYldEY27isRKG30Y6rJbgcpAjfZxXGMnLPfHN-nCaGIwNkr97NLb4BpC5vZZZTKQvh8bEzIFSxDn_gdlbxrrmVys0LRW_LYAcS-NJ37ULSA6YVx7LMvRP4ZwdhmehXHZlYLjm4vlMhKaG-cMkPyhjGWzi18J_C6GRxiGsu6bl8Doff4xaQOH0YMUMCBtgdsHZx_6cK7zFjM4PIGm7wQWAcejys8qevrWZlseic8eGCjE4YsPx0S07FOFA3FbD-EA3Ps7KX4kNbODfP9zoklo_hfBdwP9HSOtHwbp7s9rcMbKZe-M_j1IkcwRcGlyjiQO_TbvcGCYvKUwh-BdClA
&access_token=eyJraWQiOiJUTmJhREFfbDBua3RWRHdaMi12RlNUNENEVTlQM182VnBuZ3FRWmVEM0hvIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULldiRGJaOVA1VDhNcHR4cG9qR2tYMC1mRU4zcTZWdHB4MTF4LU80VFFaa2ciLCJpc3MiOiJodHRwczovL2Rldi0zOTkwNzQxNS5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE2Mzc4NjM0NTgsImV4cCI6MTYzNzg2NzA1OCwiY2lkIjoiMG9hMnZsaTM0alBsQ2I1ZWU1ZDciLCJ1aWQiOiIwMHUydmhvM2tmOWR2YnhPaDVkNyIsInNjcCI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiXSwic3ViIjoid29qbmFyNDhAZ2l0aHViLm9rdGFpZHAifQ.spsbBM-Q5mdvtYJaAuRAxzYjD-YVsKc4KBC8FfVPURTr6S4QYHqjpL0HUg084Nn9phdFZznIH845lD7lt3P83eD9_um271pujEIplfqps1fN4s4mNzsNb9aX1sYH9XpMYfqVMW1w0zpS1X35YmeAE4f3M8GXIUVcQjKHF-mqKDAUpxCkQI1rkwKyce4kPsXfapzPsgCbjDkbxNaLIm-oamFZYanKjX9TG3gYdbUO2P9RWcNMaYCcXtbZyaFPGmbo_lUZmxM-OR5Z-YzJXKSCMAWLLRbVna2Vgbytl4yOsG21XNMb5AcVHv4Du6qQjdNMFNhO0Y_bi0E1mc-M-sKNtg
&token_type=Bearer
&expires_in=3600
&scope=email+profile+openid
&state=PSILbZVle8BxGvK3sU2ID1z8rDVA2ZdPWW726JNa75svd4nUMKSFWhDFpCdHFb33
Initially used by native and mobile apps because at the time most browsers and providers were not capable of supporting PKCE.
Allows apps to use the most secure OAuth 2.0 flow in public and untrusted clients. It does this by doing some pre-req setup work before the flow and some verification at the end to effectively utilize a dynamically generated secret. This is crucial bc. it is not safe to have a fixed secret in a public client (like a SPA app).
PKCE works by:
- Having the app generate a random value before going through the flow called a
Code Verifier
. - Hashing the
Code Verifier
to obtain aCode Challenge
. - Kicks off flow with
Code Challenge
included in the query string for the request to the Auth Server. - The Auth Server stores the
Code Challenge
and after the user authenticates, redirects back to the app with an authorization code. - App makes the request to exchange the authorization code for tokens but instead of a fixed
secret, it sends the
Code Verifier
. - The Auth Server hashes the
Code Verifier
and compares it with the previously stored hashed value before returning the tokens.
Sample request to Auth Server:
https://dev-micah.okta.com/oauth2/default/v1/authorize?
client_id=0oapu4btsL2xI0y8y356&
redirect_uri=http://localhost:8080/callback&
response_type=code& // Identifies Authorization Code flow (response will be an authorization code).
response_mode=fragment&
state=MdXrGikS5LACsWs2HZFqS7IC9zMC6F9thOiWDa5gxKRqoMf7bCkTetrrwKw5JIAA&
nonce=iAXdcF77sQ2ejthPM5xZtytYUjqZkJTXcHkgdyY2NinFx6y83nKssxEzlBtvnSY2&
code_challenge=elU6u5zyqQT2f92GRQUq6PautAeNDf4DQPayyR0ek_c& // Included by Okta in PKCE extension
code_challenge_method=S256& // Included by Okta in PKCE extension
scope=openid profile email
Sample redirect from Auth Server:
http://localhost:8080/authorization-code/callback#
code=OJ6P8kNgTX9SWCPZe_FzfgqlSY095_w3Bg5mHzeyC6U
&state=8ubaBk4F0F7xZTSluxj5KDIW5BRuqIykjPKFjZACV1g3DK2z9RBPBdCe4PCBEUTh
Even though the authorization code is still returned to the browser it has 3 advantages:
- It is very short lived (60 seconds in Okta).
- It can only be used once in exchange for tokens.
- It can only be used in exchange for tokens with a secret (the Code Verifier created by okta-auth-js earlier) which malicious browser extensions would not have access to.
NOTE: Okta's library okta-auth-js
handles the exchange of the authorization code for tokens
behind the secnes.
Sample POST to /token
:
// https://dev-39907415.okta.com/oauth2/default/v1/token
client_id: 0oa2vli34jPlCb5ee5d7
redirect_uri: http://localhost:8080/authorization-code/callback
grant_type: authorization_code
code: OJ6P8kNgTX9SWCPZe_FzfgqlSY095_w3Bg5mHzeyC6U
code_verifier: 798293d431af02bdeba9d3128aa51193ae9cb2fe5c8
Sample response from Auth Server:
access_token: "eyJraWQiOiJUTmJhREFfbDBua3RWRHdaMi12RlNUNENEVTlQM182VnBuZ3FRWmVEM0hvIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULl9OLVViSjJ5VXZabFJicEh0azUtUDI0Vl9hODZkWGxBTUVtQUpoZ0xqUkkiLCJpc3MiOiJodHRwczovL2Rldi0zOTkwNzQxNS5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE2Mzc4NjQxMzQsImV4cCI6MTYzNzg2NzczNCwiY2lkIjoiMG9hMnZsaTM0alBsQ2I1ZWU1ZDciLCJ1aWQiOiIwMHUydmhvM2tmOWR2YnhPaDVkNyIsInNjcCI6WyJlbWFpbCIsInByb2ZpbGUiLCJvcGVuaWQiXSwic3ViIjoid29qbmFyNDhAZ2l0aHViLm9rdGFpZHAifQ.Q898B89mYAGsg29SG1XmZaAcpkrk1AQqvvgbl0rSWJt_7ZNxenz3tMcFPV_dUR6TCvLpzMFBV5pTlHOvGIrxB-5gC1bf0bTmA0Z9iriyxaTBI-bkpeRmTlrfdkhmHfchbYuodbQmFvqvom_zAYWEOrlaRb6mxAHDQukf7ciXVT8ywZ5Yrg8A_boFZ2llPx3pun9s-rSvYaKgaiG0pDQXojKAvaWX84V84c23s2CAZArhNizFuZ6rF_0ktPtqNAKe_sw6m14NrV2UOtNSRvcOIFf8Gz60kCxh5b_-7qhrZFM2guhMkzFzFsIp8wJjBV05Flgd1dxU464z12d15bwcLA"
expires_in: 3600
id_token: "eyJraWQiOiJUTmJhREFfbDBua3RWRHdaMi12RlNUNENEVTlQM182VnBuZ3FRWmVEM0hvIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHUydmhvM2tmOWR2YnhPaDVkNyIsIm5hbWUiOiJTenltb24gV29qbmFyIiwiZW1haWwiOiJzenltb25yd29qbmFyQGdtYWlsLmNvbSIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtMzk5MDc0MTUub2t0YS5jb20vb2F1dGgyL2RlZmF1bHQiLCJhdWQiOiIwb2EydmxpMzRqUGxDYjVlZTVkNyIsImlhdCI6MTYzNzg2NDEzNCwiZXhwIjoxNjM3ODY3NzM0LCJqdGkiOiJJRC42dnBFNGN6bVU4T2t4aDQyRDVLaldBVUhwNmczbGlrVVNyRlQ5RVBkdFBBIiwiYW1yIjpbInB3ZCJdLCJpZHAiOiIwMG8ydmhrcnMzQXVLU29ZNTVkNyIsIm5vbmNlIjoiSk00OFFSUE5UeGtGUDIweDRJUXJKeUM3M2wwZ0p2QnB4RUZSUWhkcXpkd3VaOEtmcXlDc2pBZVFrTjhycFdsaSIsInByZWZlcnJlZF91c2VybmFtZSI6Indvam5hcjQ4QGdpdGh1Yi5va3RhaWRwIiwiYXV0aF90aW1lIjoxNjM3ODYzOTMyLCJhdF9oYXNoIjoiaUNTclNMb3ZWU0xheVIzNk9aQUdRUSJ9.AVH-lKxgHYJGwDD0mvmiqZwxg9upgQx8im7qXD_JiFbHUS5PHL0d2p0DvDHLXFPXwF1-iK1I94lSXtA865chRDZSmkKfJNFV1Jj9g4dPYkQl0SB8FnKbZN3YVv-viiVuy9DquRV1kTIBWbv9C8fufGpupV8mg33WSrw1PvdfZP-MWN3zG_Og0whX6m4CXFGwIa5YjUGyiUfN9M4XK0fqMUVhjfoxN2SwJsZ142jQtBKnaVhXh3FPqCauYsyn_HCIeDgfoZ4WSlQZWjEPzdoHiD9uNfVgUb4F-Ip8e3LHjAfxj3vkyM5BCavQZOJlivQ0lC8WMZBQM2IKcdNndKRbYg"
scope: "email profile openid"
token_type: "Bearer"
NOTE: Unlike in the Implicit Flow, the request is a POST and the response comes back on the same channel. It is not a redirect like in the Implicit Flow so it will not be in your browser's history which is why the PKCE approach is much more secure than the Implicit Flow.