Skip to content

Commit

Permalink
Added customParams in authorizeUI for Implicit grant flow
Browse files Browse the repository at this point in the history
  • Loading branch information
qasim90 committed Jun 25, 2022
1 parent eeec470 commit b01578d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 125 deletions.
103 changes: 29 additions & 74 deletions lib/oauth2_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ class OAuth2Client {
String? state,
httpClient,
BaseWebAuth? webAuthClient,
Map<String, dynamic>? webAuthOpts}) async {
Map<String, dynamic>? webAuthOpts,
Map<String, dynamic>? customParams}) async {
httpClient ??= http.Client();
webAuthClient ??= this.webAuthClient;

Expand All @@ -93,22 +94,19 @@ class OAuth2Client {
scopes: scopes,
enableState: enableState,
state: state,
redirectUri: redirectUri);
redirectUri: redirectUri,
customParams: customParams);

// Present the dialog to the user
final result = await webAuthClient.authenticate(
url: authorizeUrl,
callbackUrlScheme: customUriScheme,
redirectUrl: redirectUri,
opts: webAuthOpts);
url: authorizeUrl, callbackUrlScheme: customUriScheme, redirectUrl: redirectUri, opts: webAuthOpts);

final fragment = Uri.splitQueryString(Uri.parse(result).fragment);

if (enableState) {
final checkState = fragment['state'];
if (state != checkState) {
throw Exception(
'"state" parameter in response doesn\'t correspond to the expected value');
throw Exception('"state" parameter in response doesn\'t correspond to the expected value');
}
}

Expand Down Expand Up @@ -178,22 +176,15 @@ class OAuth2Client {

/// Requests an Access Token to the OAuth2 endpoint using the Client Credentials flow.
Future<AccessTokenResponse> getTokenWithClientCredentialsFlow(
{required String clientId,
required String clientSecret,
List<String>? scopes,
httpClient}) async {
{required String clientId, required String clientSecret, List<String>? scopes, httpClient}) async {
var params = <String, String>{'grant_type': 'client_credentials'};

if (scopes != null && scopes.isNotEmpty) {
params['scope'] = serializeScopes(scopes);
}

var response = await _performAuthorizedRequest(
url: tokenUrl,
clientId: clientId,
clientSecret: clientSecret,
params: params,
httpClient: httpClient);
url: tokenUrl, clientId: clientId, clientSecret: clientSecret, params: params, httpClient: httpClient);

return http2TokenResponse(response, requestedScopes: scopes);
}
Expand Down Expand Up @@ -225,10 +216,7 @@ class OAuth2Client {

// Present the dialog to the user
final result = await webAuthClient.authenticate(
url: authorizeUrl,
callbackUrlScheme: customUriScheme,
redirectUrl: redirectUri,
opts: webAuthOpts);
url: authorizeUrl, callbackUrlScheme: customUriScheme, redirectUrl: redirectUri, opts: webAuthOpts);

return AuthorizationResponse.fromRedirectUri(result, state);
}
Expand All @@ -242,11 +230,8 @@ class OAuth2Client {
List<String>? scopes,
Map<String, dynamic>? customParams,
httpClient}) async {
final params = getTokenUrlParams(
code: code,
redirectUri: redirectUri,
codeVerifier: codeVerifier,
customParams: customParams);
final params =
getTokenUrlParams(code: code, redirectUri: redirectUri, codeVerifier: codeVerifier, customParams: customParams);

var response = await _performAuthorizedRequest(
url: tokenUrl,
Expand All @@ -261,32 +246,23 @@ class OAuth2Client {

/// Refreshes an Access Token issuing a refresh_token grant to the OAuth2 server.
Future<AccessTokenResponse> refreshToken(String refreshToken,
{httpClient,
required String clientId,
String? clientSecret,
List<String>? scopes}) async {
{httpClient, required String clientId, String? clientSecret, List<String>? scopes}) async {
final Map params = getRefreshUrlParams(refreshToken: refreshToken);

var response = await _performAuthorizedRequest(
url: _getRefreshUrl(),
clientId: clientId,
clientSecret: clientSecret,
params: params,
httpClient: httpClient);
url: _getRefreshUrl(), clientId: clientId, clientSecret: clientSecret, params: params, httpClient: httpClient);

return http2TokenResponse(response, requestedScopes: scopes);
}

/// Revokes both the Access and the Refresh tokens in the provided [tknResp]
Future<OAuth2Response> revokeToken(AccessTokenResponse tknResp,
{String? clientId, String? clientSecret, httpClient}) async {
var tokenRevocationResp = await revokeAccessToken(tknResp,
clientId: clientId, clientSecret: clientSecret, httpClient: httpClient);
var tokenRevocationResp =
await revokeAccessToken(tknResp, clientId: clientId, clientSecret: clientSecret, httpClient: httpClient);
if (tokenRevocationResp.isValid()) {
tokenRevocationResp = await revokeRefreshToken(tknResp,
clientId: clientId,
clientSecret: clientSecret,
httpClient: httpClient);
tokenRevocationResp =
await revokeRefreshToken(tknResp, clientId: clientId, clientSecret: clientSecret, httpClient: httpClient);
}

return tokenRevocationResp;
Expand Down Expand Up @@ -316,10 +292,7 @@ class OAuth2Client {
String? state,
String? codeChallenge,
Map<String, dynamic>? customParams}) {
final params = <String, dynamic>{
'response_type': responseType,
'client_id': clientId
};
final params = <String, dynamic>{'response_type': responseType, 'client_id': clientId};

if (redirectUri != null && redirectUri.isNotEmpty) {
params['redirect_uri'] = redirectUri;
Expand Down Expand Up @@ -347,14 +320,8 @@ class OAuth2Client {

/// Returns the parameters needed for the authorization code request
Map<String, dynamic> getTokenUrlParams(
{required String code,
String? redirectUri,
String? codeVerifier,
Map<String, dynamic>? customParams}) {
final params = <String, dynamic>{
'grant_type': 'authorization_code',
'code': code
};
{required String code, String? redirectUri, String? codeVerifier, Map<String, dynamic>? customParams}) {
final params = <String, dynamic>{'grant_type': 'authorization_code', 'code': code};

if (redirectUri != null && redirectUri.isNotEmpty) {
params['redirect_uri'] = redirectUri;
Expand Down Expand Up @@ -414,19 +381,16 @@ class OAuth2Client {
}
}

var response =
await httpClient.post(Uri.parse(url), body: params, headers: headers);
var response = await httpClient.post(Uri.parse(url), body: params, headers: headers);

return response;
}

Map<String, String> getAuthorizationHeader(
{required String clientId, String? clientSecret}) {
Map<String, String> getAuthorizationHeader({required String clientId, String? clientSecret}) {
var headers = <String, String>{};

if ((clientId.isNotEmpty) && (clientSecret != null)) {
var credentials =
base64.encode(utf8.encode(clientId + ':' + clientSecret));
var credentials = base64.encode(utf8.encode(clientId + ':' + clientSecret));

headers['Authorization'] = 'Basic ' + credentials;
}
Expand All @@ -436,46 +400,37 @@ class OAuth2Client {

/// Returns the parameters needed for the refresh token request
Map<String, String> getRefreshUrlParams({required String refreshToken}) {
final params = <String, String>{
'grant_type': 'refresh_token',
'refresh_token': refreshToken
};
final params = <String, String>{'grant_type': 'refresh_token', 'refresh_token': refreshToken};

return params;
}

AccessTokenResponse http2TokenResponse(http.Response response,
{List<String>? requestedScopes}) {
return AccessTokenResponse.fromHttpResponse(response,
requestedScopes: requestedScopes);
AccessTokenResponse http2TokenResponse(http.Response response, {List<String>? requestedScopes}) {
return AccessTokenResponse.fromHttpResponse(response, requestedScopes: requestedScopes);
}

String serializeScopes(List<String> scopes) {
return scopes.map((s) => s.trim()).join(scopeSeparator);
}

/// Revokes the specified token [type] in the [tknResp]
Future<OAuth2Response> _revokeTokenByType(
AccessTokenResponse tknResp, String tokenType,
Future<OAuth2Response> _revokeTokenByType(AccessTokenResponse tknResp, String tokenType,
{String? clientId, String? clientSecret, httpClient}) async {
var resp = OAuth2Response();

if (revokeUrl == null) return resp;

httpClient ??= http.Client();

var token = tokenType == 'access_token'
? tknResp.accessToken
: tknResp.refreshToken;
var token = tokenType == 'access_token' ? tknResp.accessToken : tknResp.refreshToken;

if (token != null) {
var params = {'token': token, 'token_type_hint': tokenType};

if (clientId != null) params['client_id'] = clientId;
if (clientSecret != null) params['client_secret'] = clientSecret;

http.Response response =
await httpClient.post(Uri.parse(revokeUrl!), body: params);
http.Response response = await httpClient.post(Uri.parse(revokeUrl!), body: params);

resp = http2TokenResponse(response);
}
Expand Down
72 changes: 21 additions & 51 deletions lib/oauth2_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ class OAuth2Helper {
}

if (!tknResp.isValid()) {
throw Exception(
'Provider error ${tknResp.httpStatusCode}: ${tknResp.error}: ${tknResp.errorDescription}');
throw Exception('Provider error ${tknResp.httpStatusCode}: ${tknResp.error}: ${tknResp.errorDescription}');
}

if (!tknResp.isBearer()) {
Expand Down Expand Up @@ -146,7 +145,8 @@ class OAuth2Helper {
scopes: scopes,
enableState: enableState,
webAuthClient: webAuthClient,
webAuthOpts: webAuthOpts);
webAuthOpts: webAuthOpts,
customParams: authCodeParams);
} else {
tknResp = AccessTokenResponse.errorResponse();
}
Expand All @@ -159,15 +159,12 @@ class OAuth2Helper {
}

/// Performs a refresh_token request using the [refreshToken].
Future<AccessTokenResponse> refreshToken(
AccessTokenResponse curTknResp) async {
Future<AccessTokenResponse> refreshToken(AccessTokenResponse curTknResp) async {
var tknResp;
var refreshToken = curTknResp.refreshToken!;
try {
tknResp = await client.refreshToken(refreshToken,
clientId: clientId,
clientSecret: clientSecret,
scopes: curTknResp.scope);
clientId: clientId, clientSecret: clientSecret, scopes: curTknResp.scope);
} catch (_) {
return await fetchToken();
}
Expand All @@ -188,8 +185,7 @@ class OAuth2Helper {
//Fetch another access token
tknResp = await getToken();
} else {
throw OAuth2Exception(tknResp.error,
errorDescription: tknResp.errorDescription);
throw OAuth2Exception(tknResp.error, errorDescription: tknResp.errorDescription);
}
}

Expand All @@ -204,10 +200,7 @@ class OAuth2Helper {

if (tknResp != null) {
await tokenStorage.deleteToken(scopes ?? []);
return await client.revokeToken(tknResp,
clientId: clientId,
clientSecret: clientSecret,
httpClient: httpClient);
return await client.revokeToken(tknResp, clientId: clientId, clientSecret: clientSecret, httpClient: httpClient);
} else {
return OAuth2Response();
}
Expand All @@ -220,68 +213,49 @@ class OAuth2Helper {
/// Performs a POST request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> post(String url,
{Map<String, String>? headers,
dynamic body,
http.Client? httpClient}) async {
return _request('POST', url,
headers: headers, body: body, httpClient: httpClient);
Future<http.Response> post(String url, {Map<String, String>? headers, dynamic body, http.Client? httpClient}) async {
return _request('POST', url, headers: headers, body: body, httpClient: httpClient);
}

/// Performs a PUT request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> put(String url,
{Map<String, String>? headers,
dynamic body,
http.Client? httpClient}) async {
return _request('PUT', url,
headers: headers, body: body, httpClient: httpClient);
Future<http.Response> put(String url, {Map<String, String>? headers, dynamic body, http.Client? httpClient}) async {
return _request('PUT', url, headers: headers, body: body, httpClient: httpClient);
}

/// Performs a PATCH request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> patch(String url,
{Map<String, String>? headers,
dynamic body,
http.Client? httpClient}) async {
return _request('PATCH', url,
headers: headers, body: body, httpClient: httpClient);
Future<http.Response> patch(String url, {Map<String, String>? headers, dynamic body, http.Client? httpClient}) async {
return _request('PATCH', url, headers: headers, body: body, httpClient: httpClient);
}

/// Performs a GET request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> get(String url,
{Map<String, String>? headers, http.Client? httpClient}) async {
Future<http.Response> get(String url, {Map<String, String>? headers, http.Client? httpClient}) async {
return _request('GET', url, headers: headers, httpClient: httpClient);
}

/// Performs a DELETE request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> delete(String url,
{Map<String, String>? headers, http.Client? httpClient}) async {
Future<http.Response> delete(String url, {Map<String, String>? headers, http.Client? httpClient}) async {
return _request('DELETE', url, headers: headers, httpClient: httpClient);
}

/// Performs a HEAD request to the specified [url], adding the authorization token.
///
/// If no token already exists, or if it is expired, a new one is requested.
Future<http.Response> head(String url,
{Map<String, String>? headers,
dynamic body,
http.Client? httpClient}) async {
Future<http.Response> head(String url, {Map<String, String>? headers, dynamic body, http.Client? httpClient}) async {
return _request('HEAD', url, headers: headers, httpClient: httpClient);
}

/// Common method for making http requests
/// Tries to use a previously fetched token, otherwise fetches a new token by means of a refresh flow or by issuing a new authorization flow
Future<http.Response> _request(String method, String url,
{Map<String, String>? headers,
dynamic body,
http.Client? httpClient}) async {
{Map<String, String>? headers, dynamic body, http.Client? httpClient}) async {
httpClient ??= http.Client();

headers ??= {};
Expand All @@ -292,14 +266,11 @@ class OAuth2Helper {
headers!['Authorization'] = 'Bearer ' + accessToken;

if (method == 'POST') {
resp = await httpClient!
.post(Uri.parse(url), body: body, headers: headers);
resp = await httpClient!.post(Uri.parse(url), body: body, headers: headers);
} else if (method == 'PUT') {
resp =
await httpClient!.put(Uri.parse(url), body: body, headers: headers);
resp = await httpClient!.put(Uri.parse(url), body: body, headers: headers);
} else if (method == 'PATCH') {
resp = await httpClient!
.patch(Uri.parse(url), body: body, headers: headers);
resp = await httpClient!.patch(Uri.parse(url), body: body, headers: headers);
} else if (method == 'GET') {
resp = await httpClient!.get(Uri.parse(url), headers: headers);
} else if (method == 'DELETE') {
Expand Down Expand Up @@ -343,8 +314,7 @@ class OAuth2Helper {
throw Exception('Required "clientId" parameter not set');
}

if (grantType == CLIENT_CREDENTIALS &&
(clientSecret == null || clientSecret!.isEmpty)) {
if (grantType == CLIENT_CREDENTIALS && (clientSecret == null || clientSecret!.isEmpty)) {
throw Exception('Required "clientSecret" parameter not set');
}
}
Expand Down

0 comments on commit b01578d

Please sign in to comment.