RFC7636: Proof Key for Code Exchange by OAuth Public Clients¶
This RFC 7636 is used to improve the security of Authorization Code flow for public clients by sending extra “code_challenge” and “code_verifier” to the authorization server.
The code_challenge goes to the authorization endpoint, through the
browser. The code_verifier goes to the token endpoint, from server to
server. Someone who steals the code cannot use it without the
code_verifier:
sequenceDiagram
autonumber
participant User as User's browser<br/>(User Agent)
participant Client
box rgba(80, 120, 220, 0.12) CodeChallenge
participant AS as Authorization Server
end
participant RS as Resource Server
Note over Client: Generates a code_verifier<br/>and its code_challenge
rect rgba(120, 120, 120, 0.08)
Note over User,AS: Front channel (browser)
Client-->>User: 302 Found → /authorize
User->>AS: GET /authorize (code_challenge)
Note over User,AS: The user logs in and approves
AS-->>User: 302 Found → redirect_uri?code=…
User->>Client: GET redirect_uri?code=…
end
rect rgba(120, 120, 120, 0.08)
Note over Client,RS: Back channel (server to server)
Client->>AS: POST /token (code, code_verifier)
AS-->>Client: 200 OK + access token
Client->>RS: GET /api (bearer token)
RS-->>Client: 200 OK + resource
end
The client redirects the browser to the authorization endpoint.
The request contains
code_challengeandcode_challenge_method. WithS256,code_challengeis the SHA-256 hash ofcode_verifier, in base64url.CodeChallengechecks the format ofcode_challengeand the support ofcode_challenge_method. Thesave_authorization_codemethod of the grant stores them with the code.The authorization server redirects the browser to the client with a code.
The client receives the code.
The client sends the
codeand thecode_verifier.CodeChallengecomputes the challenge fromcode_verifierand compares it with the storedcode_challenge. It returnsinvalid_grantif they differ.The authorization server returns an access token.
The client calls the API of the Resource Server with the access token, in the
Authorization: Bearerheader.The Resource Server returns the protected resource.
The /authorize, /token and /api paths are examples.
Using code_challenge in Client¶
Read the Code Challenge section in the Web Clients.
It is also possible to add code_challenge in OAuth2Session,
consider that we already have a session:
>>> from authlib.oauth2.rfc7636 import create_s256_code_challenge
>>> code_verifier = generate_token(48)
>>> code_challenge = create_s256_code_challenge(code_verifier)
>>> uri, state = session.create_authorization_url(authorize_url, code_challenge=code_challenge, code_challenge_method='S256')
>>> # visit uri, get the response
>>> authorization_response = 'https://example.com/auth?code=42..e9&state=d..t'
>>> token = session.fetch_token(token_endpoint, authorization_response=authorization_response, code_verifier=code_verifier)
API Reference¶
- class authlib.oauth2.rfc7636.CodeChallenge(required=True)¶
CodeChallenge extension to Authorization Code Grant. It is used to improve the security of Authorization Code flow for public clients by sending extra “code_challenge” and “code_verifier” to the authorization server.
The AuthorizationCodeGrant SHOULD save the
code_challengeandcode_challenge_methodinto database whensave_authorization_code. Then register this extension via:server.register_grant(AuthorizationCodeGrant, [CodeChallenge(required=True)])
- DEFAULT_CODE_CHALLENGE_METHOD = 'plain'¶
defaults to “plain” if not present in the request
- SUPPORTED_CODE_CHALLENGE_METHOD = ['plain', 'S256']¶
supported
code_challenge_method
- get_authorization_code_challenge(authorization_code)¶
Get “code_challenge” associated with this authorization code. Developers MAY re-implement it in subclass, the default logic:
def get_authorization_code_challenge(self, authorization_code): return authorization_code.code_challenge
- Parameters:
authorization_code – the instance of authorization_code
- get_authorization_code_challenge_method(authorization_code)¶
Get “code_challenge_method” associated with this authorization code. Developers MAY re-implement it in subclass, the default logic:
def get_authorization_code_challenge_method(self, authorization_code): return authorization_code.code_challenge_method
- Parameters:
authorization_code – the instance of authorization_code