authorize method
Initiates an OAuth 2.0 authorization request using Pushed Authorization Requests (PAR) with PKCE (Proof Key for Code Exchange) and DPoP (Demonstrating Proof of Possession).
This method implements the following OAuth 2.0 security features:
- PAR (RFC 9126) for secure authorization request transmission
- PKCE (RFC 7636) to prevent authorization code interception
- DPoP (RFC 9449) for proof-of-possession tokens
The flow consists of two steps:
- Pushes the authorization request parameters to the authorization server
- Returns the authorization URL with the obtained request URI
Example:
final authUrl = await authorize('shinyakato.dev');
// Redirect user to authUrl for authentication
Parameters:
identity
: The user's identifier (typically email) used as login_hint
Security measures implemented:
- Generates cryptographically secure random values for PKCE and state
- Uses SHA-256 for PKCE code challenge
- Stores DPoP nonce from server response
- Validates server response status (201 Created)
Throws:
- OAuthException: If the PAR request fails or returns unexpected status code
Returns:
- Uri: The authorization URL where the user should be redirected to complete authentication
Implementation
Future<(Uri, OAuthContext)> authorize(final String identity) async {
final codeVerifier = random(46);
final codeChallenge = hashS256(codeVerifier);
final state = random(64);
final response = await http.post(
Uri.https(service, '/oauth/par'),
body: {
'client_id': metadata.clientId,
'redirect_uri': metadata.redirectUris.firstOrNull,
'login_hint': identity,
'state': state,
'code_challenge': codeChallenge,
'code_challenge_method': 'S256',
'response_type': 'code',
'scope': metadata.scope,
},
);
if (response.statusCode != 201) {
throw OAuthException(response.body);
}
return (
Uri.https(
service,
'/oauth/authorize',
{
'client_id': metadata.clientId,
'request_uri': jsonDecode(response.body)['request_uri']!,
},
),
OAuthContext(
codeVerifier: codeVerifier,
state: state,
dpopNonce: response.headers['dpop-nonce']!,
)
);
}