mirror of
https://github.com/foss42/apidash.git
synced 2025-12-05 12:34:26 +08:00
Implement OAuth2 secure token storage with automatic migration
Co-authored-by: animator <615622+animator@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:oauth2/oauth2.dart' as oauth2;
|
||||
import '../../models/auth/auth_oauth2_model.dart';
|
||||
import '../../services/http_client_manager.dart';
|
||||
import '../../services/oauth_callback_server.dart';
|
||||
import '../../services/oauth2_secure_storage.dart';
|
||||
import '../platform_utils.dart';
|
||||
|
||||
/// Advanced OAuth2 authorization code grant handler that returns both the client and server
|
||||
@@ -23,13 +24,47 @@ Future<(oauth2.Client, OAuthCallbackServer?)> oAuth2AuthorizationCodeGrant({
|
||||
String? state,
|
||||
String? scope,
|
||||
}) async {
|
||||
// Check for existing credentials first
|
||||
// Check for existing credentials first - try secure storage, then file
|
||||
// Try secure storage first (preferred method)
|
||||
try {
|
||||
final secureCredJson = await OAuth2SecureStorage.retrieveCredentials(
|
||||
clientId: identifier,
|
||||
tokenUrl: tokenEndpoint.toString(),
|
||||
);
|
||||
|
||||
if (secureCredJson != null) {
|
||||
final credentials = oauth2.Credentials.fromJson(secureCredJson);
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
return (
|
||||
oauth2.Client(credentials, identifier: identifier, secret: secret),
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Secure storage failed, try file fallback
|
||||
}
|
||||
|
||||
// Fallback to file-based storage for backward compatibility
|
||||
if (credentialsFile != null && await credentialsFile.exists()) {
|
||||
try {
|
||||
final json = await credentialsFile.readAsString();
|
||||
final credentials = oauth2.Credentials.fromJson(json);
|
||||
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
// Migrate to secure storage for future use
|
||||
try {
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: identifier,
|
||||
tokenUrl: tokenEndpoint.toString(),
|
||||
credentialsJson: json,
|
||||
);
|
||||
// Delete old file after successful migration
|
||||
await credentialsFile.delete();
|
||||
} catch (e) {
|
||||
// Migration failed, keep using file
|
||||
}
|
||||
|
||||
return (
|
||||
oauth2.Client(credentials, identifier: identifier, secret: secret),
|
||||
null,
|
||||
@@ -124,8 +159,22 @@ Future<(oauth2.Client, OAuthCallbackServer?)> oAuth2AuthorizationCodeGrant({
|
||||
callbackUriParsed.queryParameters,
|
||||
);
|
||||
|
||||
if (credentialsFile != null) {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
// Store credentials securely (preferred method)
|
||||
try {
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: identifier,
|
||||
tokenUrl: tokenEndpoint.toString(),
|
||||
credentialsJson: client.credentials.toJson(),
|
||||
);
|
||||
} catch (e) {
|
||||
// Secure storage failed, fallback to file if available
|
||||
if (credentialsFile != null) {
|
||||
try {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
} catch (fileError) {
|
||||
// Both storage methods failed - credentials won't persist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (client, callbackServer);
|
||||
@@ -150,13 +199,46 @@ Future<oauth2.Client> oAuth2ClientCredentialsGrantHandler({
|
||||
required AuthOAuth2Model oauth2Model,
|
||||
required File? credentialsFile,
|
||||
}) async {
|
||||
// Try to use saved credentials
|
||||
// Try secure storage first
|
||||
try {
|
||||
final secureCredJson = await OAuth2SecureStorage.retrieveCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
);
|
||||
|
||||
if (secureCredJson != null) {
|
||||
final credentials = oauth2.Credentials.fromJson(secureCredJson);
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
return oauth2.Client(
|
||||
credentials,
|
||||
identifier: oauth2Model.clientId,
|
||||
secret: oauth2Model.clientSecret,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Secure storage failed, try file fallback
|
||||
}
|
||||
|
||||
// Fallback to file-based storage for backward compatibility
|
||||
if (credentialsFile != null && await credentialsFile.exists()) {
|
||||
try {
|
||||
final json = await credentialsFile.readAsString();
|
||||
final credentials = oauth2.Credentials.fromJson(json);
|
||||
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
// Migrate to secure storage
|
||||
try {
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
credentialsJson: json,
|
||||
);
|
||||
await credentialsFile.delete();
|
||||
} catch (e) {
|
||||
// Migration failed
|
||||
}
|
||||
|
||||
return oauth2.Client(
|
||||
credentials,
|
||||
identifier: oauth2Model.clientId,
|
||||
@@ -184,12 +266,22 @@ Future<oauth2.Client> oAuth2ClientCredentialsGrantHandler({
|
||||
httpClient: baseClient,
|
||||
);
|
||||
|
||||
// Store credentials securely
|
||||
try {
|
||||
if (credentialsFile != null) {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
}
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
credentialsJson: client.credentials.toJson(),
|
||||
);
|
||||
} catch (e) {
|
||||
// Ignore credential saving errors
|
||||
// Secure storage failed, try file fallback
|
||||
if (credentialsFile != null) {
|
||||
try {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
} catch (fileError) {
|
||||
// Both storage methods failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the HTTP client
|
||||
@@ -207,13 +299,46 @@ Future<oauth2.Client> oAuth2ResourceOwnerPasswordGrantHandler({
|
||||
required AuthOAuth2Model oauth2Model,
|
||||
required File? credentialsFile,
|
||||
}) async {
|
||||
// Try to use saved credentials
|
||||
// Try secure storage first
|
||||
try {
|
||||
final secureCredJson = await OAuth2SecureStorage.retrieveCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
);
|
||||
|
||||
if (secureCredJson != null) {
|
||||
final credentials = oauth2.Credentials.fromJson(secureCredJson);
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
return oauth2.Client(
|
||||
credentials,
|
||||
identifier: oauth2Model.clientId,
|
||||
secret: oauth2Model.clientSecret,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Secure storage failed, try file fallback
|
||||
}
|
||||
|
||||
// Fallback to file-based storage for backward compatibility
|
||||
if (credentialsFile != null && await credentialsFile.exists()) {
|
||||
try {
|
||||
final json = await credentialsFile.readAsString();
|
||||
final credentials = oauth2.Credentials.fromJson(json);
|
||||
|
||||
if (credentials.accessToken.isNotEmpty && !credentials.isExpired) {
|
||||
// Migrate to secure storage
|
||||
try {
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
credentialsJson: json,
|
||||
);
|
||||
await credentialsFile.delete();
|
||||
} catch (e) {
|
||||
// Migration failed
|
||||
}
|
||||
|
||||
return oauth2.Client(
|
||||
credentials,
|
||||
identifier: oauth2Model.clientId,
|
||||
@@ -247,12 +372,22 @@ Future<oauth2.Client> oAuth2ResourceOwnerPasswordGrantHandler({
|
||||
httpClient: baseClient,
|
||||
);
|
||||
|
||||
// Store credentials securely
|
||||
try {
|
||||
if (credentialsFile != null) {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
}
|
||||
await OAuth2SecureStorage.storeCredentials(
|
||||
clientId: oauth2Model.clientId,
|
||||
tokenUrl: oauth2Model.accessTokenUrl,
|
||||
credentialsJson: client.credentials.toJson(),
|
||||
);
|
||||
} catch (e) {
|
||||
// Ignore credential saving errors
|
||||
// Secure storage failed, try file fallback
|
||||
if (credentialsFile != null) {
|
||||
try {
|
||||
await credentialsFile.writeAsString(client.credentials.toJson());
|
||||
} catch (fileError) {
|
||||
// Both storage methods failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up the HTTP client
|
||||
|
||||
Reference in New Issue
Block a user