Skip to content
Merged
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
47 changes: 14 additions & 33 deletions lib/auth/providers/openid_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,6 @@ final tokenProvider = Provider((ref) {
});

class OpenIdTokenProvider extends Notifier<AsyncValue<models.TokenResponse>> {
final String tokenKey = "token";
final String refreshTokenKey = "refresh_token";
OpenIdTokenProvider() : super();

AuthRepository get userRepository => ref.read(authRepositoryProvider);

@override
Expand All @@ -103,11 +99,11 @@ class OpenIdTokenProvider extends Notifier<AsyncValue<models.TokenResponse>> {
return const AsyncValue.loading();
}

Future getTokenFromRequest() async {
Future<void> getTokenFromRequest() async {
state = const AsyncValue.loading();
try {
final tokenResponse = await userRepository.getTokenFromRequest();
if (tokenResponse.accessToken != "") {
if (tokenResponse.accessToken.isNotEmpty) {
state = AsyncValue.data(tokenResponse);
} else {
state = const AsyncValue.error("Error", StackTrace.empty);
Expand All @@ -117,27 +113,11 @@ class OpenIdTokenProvider extends Notifier<AsyncValue<models.TokenResponse>> {
}
}

Future getTokenFromStorage() async {
Future<void> getTokenFromStorage() async {
state = const AsyncValue.loading();
try {
final tokenResponse = await userRepository.getTokenFromStorage();
if (tokenResponse.accessToken != "") {
state = AsyncValue.data(tokenResponse);
} else {
state = const AsyncValue.error("Error", StackTrace.empty);
}
} catch (e, s) {
state = AsyncValue.error(e, s);
}
}

Future<void> getAuthToken(String authorizationToken) async {
state = const AsyncValue.loading();
try {
final tokenResponse = await userRepository.getAuthToken(
authorizationToken,
);
if (tokenResponse.accessToken != "") {
if (tokenResponse.accessToken.isNotEmpty) {
state = AsyncValue.data(tokenResponse);
} else {
state = const AsyncValue.error("Error", StackTrace.empty);
Expand All @@ -147,21 +127,22 @@ class OpenIdTokenProvider extends Notifier<AsyncValue<models.TokenResponse>> {
}
}

Future<bool> refreshToken() async {
state = const AsyncValue.loading();
Future<String> refreshAccessToken() async {
try {
final tokenResponse = await userRepository.refreshToken();
if (tokenResponse.accessToken != "") {
state = AsyncValue.data(tokenResponse);
return true;
if (tokenResponse.accessToken.isEmpty) {
throw Exception('Received an empty access token');
}
state = const AsyncValue.error("Error", StackTrace.empty);
return false;
state = AsyncValue.data(tokenResponse);
return tokenResponse.accessToken;
} catch (e, s) {
state = AsyncValue.error(e, s);
return false;
rethrow;
}
}

void deleteToken() => userRepository.deleteToken();
Future<void> deleteToken() async {
await userRepository.deleteToken();
state = const AsyncValue.error('Logged out', StackTrace.empty);
}
}
176 changes: 78 additions & 98 deletions lib/auth/repository/auth_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class AuthRepository {
final FlutterAppAuth appAuth = const FlutterAppAuth();
final CacheManager cacheManager = CacheManager();
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
Future<models.TokenResponse>? _refreshInProgress;
final Base64Codec base64 = const Base64Codec.urlSafe();
final String tokenName = "my_ecl_auth_token";
final String clientId = "Titan";
Expand Down Expand Up @@ -67,7 +68,7 @@ class AuthRepository {
},
);
if (response.isSuccessful && response.body != null) {
storeToken(response.body!);
await storeToken(response.body!);
return response.body!;
} else {
throw Exception('Wrong credentials');
Expand Down Expand Up @@ -103,6 +104,9 @@ class AuthRepository {
}
},
loginCallback: (String data) async {
if (receivedCode) {
return;
}
receivedCode = true;
try {
completer.complete(await login(data));
Expand All @@ -125,132 +129,108 @@ class AuthRepository {
),
);
if (resp.accessToken != null && resp.refreshToken != null) {
await _secureStorage.write(key: tokenName, value: resp.refreshToken);
tokenResponse = models.TokenResponse(
accessToken: resp.accessToken!,
refreshToken: resp.refreshToken!,
);
storeToken(tokenResponse);
await storeToken(tokenResponse);
return tokenResponse;
} else {
throw Exception('Wrong credentials');
}
}
}

Future<models.TokenResponse> getTokenFromStorage() async {
models.TokenResponse tokenResponse = models.TokenResponse.empty();
return _secureStorage.read(key: tokenName).then((token) async {
if (token != null) {
try {
if (kIsWeb) {
final response = await openIdRepository.authTokenPost(
body: {
"client_id": clientId,
"code": token,
"redirect_uri": "",
"code_verifier": "",
"grant_type": "refresh_token",
"refresh_token": token,
},
);
if (response.isSuccessful && response.body != null) {
tokenResponse = response.body!;
storeToken(tokenResponse);
}
} else {
final resp = await appAuth.token(
TokenRequest(
clientId,
redirectUrl,
serviceConfiguration: authorizationServiceConfiguration,
scopes: scopes,
refreshToken: token,
),
);
if (resp.accessToken != null && resp.refreshToken != null) {
tokenResponse = models.TokenResponse(
accessToken: resp.accessToken!,
refreshToken: resp.refreshToken!,
);
storeToken(tokenResponse);
} else {
throw Exception('Wrong credentials');
}
}
} on TimeoutException catch (_) {
throw Exception('No response from server');
} catch (e) {
rethrow;
}
} else {
throw Exception('Wrong credentials');
Future<models.TokenResponse> getTokenFromStorage() => refreshToken();

Future<models.TokenResponse> refreshToken() {
final refreshInProgress = _refreshInProgress;
if (refreshInProgress != null) {
return refreshInProgress;
}

final refresh = _refreshStoredToken();
_refreshInProgress = refresh;
return refresh.whenComplete(() {
if (identical(_refreshInProgress, refresh)) {
_refreshInProgress = null;
}
return tokenResponse;
});
}

Future<models.TokenResponse> getAuthToken(String authorizationToken) async {
models.TokenResponse tokenResponse = models.TokenResponse.empty();
appAuth
.token(
Future<models.TokenResponse> _refreshStoredToken() async {
final refreshToken = await _secureStorage.read(key: tokenName);
if (refreshToken == null || refreshToken.isEmpty) {
throw Exception('No refresh token found');
}

try {
late final models.TokenResponse tokenResponse;
if (kIsWeb) {
final response = await openIdRepository.authTokenPost(
body: {
"client_id": clientId,
"grant_type": "refresh_token",
"refresh_token": refreshToken,
},
);
if (!response.isSuccessful || response.body == null) {
if (response.statusCode == 400 || response.statusCode == 401) {
await _secureStorage.delete(key: tokenName);
}
throw Exception('Unable to refresh credentials');
}
tokenResponse = response.body!;
} else {
final resp = await appAuth.token(
TokenRequest(
clientId,
redirectUrl,
serviceConfiguration: authorizationServiceConfiguration,
scopes: scopes,
authorizationCode: authorizationToken,
refreshToken: refreshToken,
),
)
.then((resp) {
if (resp.accessToken != null && resp.refreshToken != null) {
tokenResponse = models.TokenResponse(
accessToken: resp.accessToken!,
refreshToken: resp.refreshToken!,
);
storeToken(tokenResponse);
} else {
throw Exception('Wrong credentials');
}
});
return tokenResponse;
}

Future<models.TokenResponse> refreshToken() async {
models.TokenResponse tokenResponse = models.TokenResponse.empty();
if (tokenResponse.refreshToken != "") {
final resp = await appAuth.token(
TokenRequest(
clientId,
redirectUrl,
serviceConfiguration: authorizationServiceConfiguration,
scopes: scopes,
refreshToken: tokenResponse.refreshToken,
),
);
if (resp.accessToken == null || resp.refreshToken == null) {
return tokenResponse;
);
if (resp.accessToken == null || resp.refreshToken == null) {
throw Exception('Unable to refresh credentials');
}
tokenResponse = models.TokenResponse(
accessToken: resp.accessToken!,
refreshToken: resp.refreshToken!,
);
}
tokenResponse = models.TokenResponse(
accessToken: resp.accessToken!,
refreshToken: resp.refreshToken!,
);
storeToken(tokenResponse);

await storeToken(tokenResponse);
return tokenResponse;
} on FlutterAppAuthPlatformException catch (error) {
final oauthError = error.platformErrorDetails.error;
if (oauthError == FlutterAppAuthOAuthError.invalidGrant ||
oauthError == FlutterAppAuthOAuthError.invalidRequest) {
await _secureStorage.delete(key: tokenName);
}
rethrow;
} on TimeoutException catch (_) {
throw Exception('No response from server');
}
return tokenResponse;
}

void storeToken(models.TokenResponse tokenResponse) {
if (tokenResponse.accessToken != "" && tokenResponse.refreshToken != "") {
_secureStorage.write(key: tokenName, value: tokenResponse.refreshToken);
Future<void> storeToken(models.TokenResponse tokenResponse) async {
if (tokenResponse.accessToken.isEmpty ||
tokenResponse.refreshToken.isEmpty) {
throw Exception('Invalid token response');
}
await _secureStorage.write(
key: tokenName,
value: tokenResponse.refreshToken,
);
}

void deleteToken() {
_secureStorage.delete(key: tokenName);
cacheManager.deleteCache(tokenName);
cacheManager.deleteCache("id");
Future<void> deleteToken() async {
await Future.wait([
_secureStorage.delete(key: tokenName),
cacheManager.deleteCache(tokenName),
cacheManager.deleteCache("id"),
]);
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/mypayment/ui/pages/main_page/main_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class PaymentMainPage extends HookConsumerWidget {
controller: ScrollController(),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.only(bottom: 60),
child: TOSDialogBox(
descriptions: tos.maybeWhen(
orElse: () => '',
Expand Down
5 changes: 3 additions & 2 deletions lib/navigation/ui/quit_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ class QuitDialog extends HookConsumerWidget {
child: CustomDialogBox(
descriptions: AppLocalizations.of(context)!.drawerLoginOut,
title: AppLocalizations.of(context)!.drawerLogOut,
onYes: () {
auth.deleteToken();
onYes: () async {
await auth.deleteToken();
if (!context.mounted) return;
if (!kIsWeb) {
ref.watch(firebaseTokenExpirationProvider.notifier).reset();
}
Expand Down
5 changes: 3 additions & 2 deletions lib/settings/ui/pages/main_page/main_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,9 @@ class SettingsMainPage extends HookConsumerWidget {
description:
localizeWithContext.settingsLogOutDescription,
title: localizeWithContext.settingsLogOut,
onYes: () {
auth.deleteToken();
onYes: () async {
await auth.deleteToken();
if (!context.mounted) return;
if (!kIsWeb) {
ref
.watch(firebaseTokenExpirationProvider.notifier)
Expand Down
Loading
Loading