,d 88
88 88
MM88MMM ,adPPYba, 88 ,d8 ,adPPYba, 8b,dPPYba, ,adPPYba,
88 a8" "8a 88 ,a8" a8P_____88 88P' `"8a I8[ ""
88 8b d8 8888[ 8PP""""""" 88 88 `"Y8ba,
88, "8a, ,a8" 88`"Yba, "8b, ,aa 88 88 aa ]8I
"Y888 `"YbbdP"' 88 `Y8a `"Ybbd8"' 88 88 `"YbbdP"'
https://github.com/mokhan/token-talk
Why? Then How.
-
- Authn vs Authz
-
- Tokens
-
- Roles
-
- Protocol Flow
-
- Grant Types
-
- Examples
- Authentication: to verify the identity of who you are.
- Authorization: to verify that you should be granted access to a resource or action.
Example 1: Flying on a plane
- passport: authentication
- plane ticket: authorization
Example 2: Riding the bus
A transit token authorizes you to ride the bus for 90 minutes. Proof of identity is not required.
+------------------------------+
| transit ticket |
| |
| |
| |
| expires: 90 minutes |
+------------------------------+
Transferring the token to someone else, authorizes them to ride the bus for 90 minutes. You're not supposed to transfer your transit token but detection is difficult.
+------------------------------+
| transit ticket |
| |
| |
| |
| expires: 90 minutes |
+------------------------------+
- Access Token: Used to access a resource.
- Refresh Token: Exchanged for a new access/refresh token.
The purpose of the access token is to allow clients to access a protected resource scoped to the privileges defined by the token and scope.
The access token
represents a subject, audience, issuer and expiration.
+------------------------------+
| transit ticket |
| |
| |
| |
| expires: 90 minutes |
+------------------------------+
Subject: Ticket bearer Audience: Bus Driver Issuer: Calgary Transit Expiration: 90 minutes from when the ticket was purchased.
+------------------------------+
| transit ticket |
| |
| |
| |
| expires: 90 minutes |
+------------------------------+
Stateful: A stateful token is one where the token needs to be looked up in a database to honour it.
Stateless: A stateless token has all the information encoded in the token.
Example: Concert ticket
When you enter a concert venue and you are asked to present your ticket. They will likely scan your ticket to verify that the ticket is legit. The scan will need to verify that the ticket is in a database.
+------------------------------+
| KRS-ONE |
| |
| |
| |
| |
| |XXXX|XXXX|XXXX|XXXX| |
+------------------------------+
Example: Calgary Transit Ticket
When you board a bus in Calgary, you must show the driver the ticket. All the data the driver needs to admit you is in the ticket itself.
+------------------------------+
| transit ticket |
| |
| |
| |
| expires: 90 minutes |
+------------------------------+
JSON web tokens allow us to create stateless tokens that encode the necessary information into the token.
{header}.{body}.{signature}
Example Token
{header}.{body}.{signature}
eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE1NTMyMDYx<snip>.BrWtDArYiut47Oo76UTD<snip>
JSON Web Signature
{
"alg": "RS256"
}
JWT Claims
{
"exp": 1553206143,
"iat": 1553119743,
"iss": "https://shiro.test/metadata",
"nbf": 1553119743,
"jti": "30ee4f06-3e2b-4ef4-961e-5a1dfd530ca5",
"sub": "d98ecc05-eab8-4683-8288-249312d3f592",
}
An access token
can expire. When an access token
expires a
client can exchange a refresh token
to gain a new access token
and refresh token
.
The purpose of the refresh token
is to allow a client to get a new
access token
and refresh token
pair. Once a refresh token
is used
it cannot be re-used.
+------------------------------+
| Credit Card |
| |
| XXXX-XXXX-XXXX-XXXX |
| |
| expires: 2024-01 |
+------------------------------+
- Client: Your service, web app, SPA, mobile app.
- Resource Owner: The HUMAN!
- Resource Server: The API
- Authorization Server: The OAuth 2.0 server.
OAuth 2 is a delegation protocol. The client
does not know the
credentials of the resource owner
but can access resources on it's
behalf.
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
OAuth 2 is a delegation protocol. The client
does not know the
credentials of the resource owner
but can access resources on it's
behalf.
+--------+ +---------------+
| |--(A)- Authorization Request ->| |
| | | HUMAN |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| |
| my app | | auth.amp.* |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| |
| | | api.amp.* |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
Short circuit for SAML service providers.
+--------+ +---------------+
| | | |
| | | HUMAN |
| | -- | |
| | | +---------------+
| | (A) SAML Authentication |
| | | +---------------+
| | -->| |
| my app | | auth.amp.* |
| |<-(B)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(C)----- Access Token ------>| |
| | | api.amp.* |
| |<-(D)--- Protected Resource ---| |
+--------+ +---------------+
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
- Authorization Code: for web apps
- Implicit: for single page apps.
- Password Credentials: for trusted clients.
- Client Credentials: for service authentication.
- Refresh: for exchanging a refresh token for an access token.
- Extensions: SAML bearer, JWT bearer
For server based applications where a client id
and client secret
can be stored securely.
This uses a redirect flow that depends on the user agent having access to both the authorization server and the client web app.
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
https://www.example.com/oauth/authorize
?response_type=code
&client_id=client_id
&redirect_uri=https://www.example.org/oauth/callback
&scope='read:scim.me write:scim.me'
-----------------
Login
username: xxxxxxx
password: xxxxxx
[login]
-----------------
----------------------------------------------------
`client X` would like the following scopes:
read your profile information (read:scim.me)
write your profile information (write:scim.me)
[okay]
----------------------------------------------------
https://www.example.org/oauth/callback
?grant_type=authorization_code
&code=secret
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| | | | | |
| | | | | |
| | | Resource | | Authorization |
| Client | | Server | | Server |
| | | | | |
| | | | | |
| | | | | |
| | +----------+ | |
| | | |
| | | |
| | | |
| | | |
+--------+ +---------------+
$ curl https://www.example.com/oauth/tokens \
-X POST \
-d '{"grant_type":"authorization_code","code":"secret"}' \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Basic base64(client_id:client_secret)"
200 OK
Cache-Control: private, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
{
"access_token": "eyJhbGciOiJSUzI1NiJ9",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "eyJleHAiOjE1NDA5M"
}
This grant can be used by a client to exchange a
refresh token
for a new access token
and refresh token
.
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
POST /token HTTP/1.1
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Response:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}
GET /api/policies/
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9
Accept: application/json
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json
[
{ "name": "Audit" },
{ "name": "Protect" },
]
_,.-"T
_.--{~ :l
c" `. :I
| .-"~-.\ l .--.
| Y_r--. Y) ___I ,-"(~\ Y
|[__L__/ j"~=__]~_~\." _/
___| \.__.r--<~__.T T/ "~/
'--cl___/\ ( () ).,_L_]}--{
`--' `-^--^\ /___"(~\ Y
"~7/ \ " `/
// //]--[
/> oX |: L
// / `| o\
//. / I [
/ \]/ l: |
Y.// `|_I
I_Z L :]
/".-7 [n]l
Y / / I //
|] / /]"/
L:/ //./
[_7 _ // /
_ ,-="_"^K_/
[ ][.-~" ~"-.]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace :token do
desc 'register a new service access/refresh token'
task :register, [:token] => [:environment] do |task, args|
require 'register_token'
puts RegisterToken.new.run(service: :shiro, token: args[:token]).inspect
end
desc 'exchange refresh token for a new access token'
task renew: [:environment] do
refresh_token = Token.refresh.active.order(created_at: :desc).first
refresh_token&.exchange!
end
end
class Token < ApplicationRecord
def exchange!(client: token_exchange_client)
response = client.exchange(value)
transaction do
destroy!
Token.create!(
service: service,
token_type: :access,
expires_at: response[:expires_in].to_i.seconds.from_now,
value: response[:access_token]
)
Token.create!(
service: service,
token_type: :refresh,
expires_at: response[:expires_in].to_i.seconds.from_now,
value: response[:refresh_token]
)
end
end
end
You can customize Lambda functions to extend Secrets Manager rotation to other secret types, such as API keys and OAuth tokens used to authenticate users to mobile applications.
An access token
decouples a resource owners credentials from the
authorization that it is delegating to a client to access protected
resources from a resource server. A refresh token
can be used by a
client to gain a new access token
and refresh token
.
The exchange process can be triggered when an access token
expires or
is revoked.
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+
https://github.com/mokhan/token-talk
References: