Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions lib/oauth2_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:oauth2_client/access_token_response.dart';
import 'package:oauth2_client/authorization_response.dart';
import 'package:oauth2_client/oauth2_response.dart';
import 'package:oauth2_client/src/oauth2_utils.dart';
import 'package:oauth2_client/tiktok_oauth2_client.dart';
import 'package:random_string/random_string.dart';

// import 'package:oauth2_client/src/web_auth.dart';
Expand Down Expand Up @@ -48,6 +49,7 @@ class OAuth2Client {
String? revokeUrl;
String authorizeUrl;
String scopeSeparator;
late String clientKey;

BaseWebAuth webAuthClient = createWebAuth();
CredentialsLocation credentialsLocation;
Expand All @@ -70,7 +72,13 @@ class OAuth2Client {
required this.redirectUri,
required this.customUriScheme,
this.credentialsLocation = CredentialsLocation.header,
this.scopeSeparator = ' '});
this.scopeSeparator = ' '}) {
clientKey = _getClientKey(this);
}

static String _getClientKey(OAuth2Client client) {
return client is TikTokOAuth2Client ? 'client_key' : 'client_id';
}

/// Requests an Access Token to the OAuth2 endpoint using the Implicit grant flow (https://tools.ietf.org/html/rfc6749#page-31)
Future<AccessTokenResponse> getTokenWithImplicitGrantFlow(
Expand Down Expand Up @@ -334,7 +342,7 @@ class OAuth2Client {
Map<String, dynamic>? customParams}) {
final params = <String, dynamic>{
'response_type': responseType,
'client_id': clientId
clientKey: clientId
};

if (redirectUri != null && redirectUri.isNotEmpty) {
Expand Down Expand Up @@ -380,7 +388,7 @@ class OAuth2Client {
//If a client secret has been specified, it will be sent in the "Authorization" header instead of a body parameter...
if (clientSecret == null || clientSecret.isEmpty) {
if (clientId != null && clientId.isNotEmpty) {
params['client_id'] = clientId;
params[clientKey] = clientId;
}
}
*/
Expand Down Expand Up @@ -413,7 +421,7 @@ class OAuth2Client {
//If a client secret has been specified, it will be sent in the "Authorization" header instead of a body parameter...
if (clientSecret == null) {
if (clientId.isNotEmpty) {
params['client_id'] = clientId;
params[clientKey] = clientId;
}
} else {
switch (credentialsLocation) {
Expand All @@ -424,7 +432,7 @@ class OAuth2Client {
));
break;
case CredentialsLocation.body:
params['client_id'] = clientId;
params[clientKey] = clientId;
params['client_secret'] = clientSecret;
break;
}
Expand Down Expand Up @@ -486,7 +494,7 @@ class OAuth2Client {
if (token != null) {
var params = {'token': token, 'token_type_hint': tokenType};

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

http.Response response =
Expand Down
95 changes: 95 additions & 0 deletions lib/tiktok_oauth2_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'package:oauth2_client/access_token_response.dart';
import 'package:oauth2_client/oauth2_client.dart';

class TikTokOAuth2Client extends OAuth2Client {
TikTokOAuth2Client(
{required String redirectUri, required String customUriScheme})
: super(
authorizeUrl: 'https://www.tiktok.com/v2/auth/authorize',
tokenUrl: 'https://open.tiktokapis.com/v2/oauth/token/',
revokeUrl: 'https://open.tiktokapis.com/v2/oauth/revoke/',
scopeSeparator: ',',
credentialsLocation: CredentialsLocation.body,
redirectUri: redirectUri,
customUriScheme: customUriScheme,
);

@override
Future<AccessTokenResponse> requestAccessToken({
required String code,
required String clientId,
String? clientSecret,
String? codeVerifier,
List<String>? scopes,
Map<String, dynamic>? customParams,
Map<String, String>? customHeaders,
httpClient,
}) async {
final params = getTokenUrlParams(
code: code,
redirectUri: redirectUri,
codeVerifier: codeVerifier,
customParams: customParams,
);

final headers = {
...?customHeaders,
...{'Content-Type': 'application/x-www-form-urlencoded'}
};

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

return http2TokenResponse(response, requestedScopes: scopes);
}

Future<http.Response> _performAuthorizedRequest({
required String url,
required String clientId,
String? clientSecret,
Map? params,
Map<String, String>? headers,
http.Client? httpClient,
}) async {
final dio = Dio();

headers ??= {};
params ??= {};

//If a client secret has been specified, it will be sent in the "Authorization" header instead of a body parameter...
if (clientSecret == null) {
if (clientId.isNotEmpty) {
params[clientKey] = clientId;
}
} else {
switch (credentialsLocation) {
case CredentialsLocation.header:
headers.addAll(getAuthorizationHeader(
clientId: clientId,
clientSecret: clientSecret,
));
break;
case CredentialsLocation.body:
params[clientKey] = clientId;
params['client_secret'] = clientSecret;
break;
}
}

var response = await dio.post<Map<String, dynamic>>(url,
data: params,
options: Options(headers: headers, responseType: ResponseType.json));

return http.Response(jsonEncode(response.data), response.statusCode ?? 0);
}
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies:
http: ^1.1.0
meta: ^1.12.0
random_string: ^2.3.1
dio: ^5.8.0+1

dev_dependencies:
flutter_test:
Expand Down
Loading