Skip to content

Commit

Permalink
Change logout to revokeSessions and add revokeOtherSessions option (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
itaihanski authored Nov 24, 2024
1 parent 697ef4d commit d5d664f
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,19 @@ When the user wants to sign out of the application we revoke the
active session and clear it from the session manager:

```dart
final refreshJwt = Descope.sessionManager.session?.refreshJwt;
final refreshJwt = Descope.sessionManager.session?.refreshToken.jwt;
if (refreshJwt != null) {
Descope.auth.logout(refreshJwt);
Descope.sessionManager.clearSession();
try {
Descope.auth.revokeSessions(RevokeType.currentSession, refreshJwt);
} catch (e) {
// handle errors
}
}
```

It is also possible to revoke all sessions by providing the appropriate `RevokeType` parameter.

You can customize how the `DescopeSessionManager` behaves by using
your own `storage` and `lifecycle` objects. See the documentation
for more details.
Expand Down
6 changes: 4 additions & 2 deletions lib/src/internal/http/descope_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,9 @@ class DescopeClient extends HttpClient {
return post('auth/refresh', JWTServerResponse.decoder, headers: authorization(refreshJwt));
}

Future<void> logout(String refreshJwt) {
return post('auth/logout', emptyResponse, headers: authorization(refreshJwt));
Future<void> logout(RevokeType revokeType, String refreshJwt) {
final route = revokeType == RevokeType.currentSession ? 'auth/logout' : 'auth/logoutall';
return post(route, emptyResponse, headers: authorization(refreshJwt));
}

// Internal
Expand Down Expand Up @@ -384,6 +385,7 @@ extension on SignInOptions {
'stepup': stepupRefreshJwt != null ? true : null,
'mfa': mfaRefreshJwt != null ? true : null,
'customClaims': customClaims.isNotEmpty ? customClaims : null,
'revokeOtherSessions': revokeOtherSessions,
};
}
}
Expand Down
12 changes: 10 additions & 2 deletions lib/src/internal/routes/auth.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import '/src/internal/http/descope_client.dart';
import '/src/sdk/routes.dart';
import '/src/types/others.dart';
import '/src/types/responses.dart';
import '/src/types/user.dart';
import '../http/descope_client.dart';
import 'shared.dart';

class Auth implements DescopeAuth {
Expand All @@ -19,8 +20,15 @@ class Auth implements DescopeAuth {
return (await client.refresh(refreshJwt)).toRefreshResponse();
}

@override
Future<void> revokeSessions(RevokeType revokeType, String refreshJwt) async {
return client.logout(revokeType, refreshJwt);
}

// Deprecated

@override
Future<void> logout(String refreshJwt) {
return client.logout(refreshJwt);
return revokeSessions(RevokeType.currentSession, refreshJwt);
}
}
26 changes: 26 additions & 0 deletions lib/src/sdk/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,33 @@ abstract class DescopeAuth {
/// or is about expire.
Future<RefreshResponse> refreshSession(String refreshJwt);

/// It's a good security practice to remove refresh JWTs from the Descope servers if
/// they become redundant before expiry. This function will called with a [RevokeType], usually [RevokeType.currentSession],
/// and a valid refresh JWT when the user wants to sign out of the application. For example:
///
/// void logout() {
/// // clear the session locally from the app and spawn a background task to revoke
/// // the refreshJWT from the Descope servers without waiting for the call to finish
/// final refreshJwt = Descope.sessionManager.session?.refreshToken.jwt;
/// if (refreshJwt != null) {
/// Descope.sessionManager.clearSession();
/// try {
/// Descope.auth.revokeSessions(RevokeType.currentSession, refreshJwt);
/// } catch (e) {
/// // handle errors
/// }
/// showLaunchScreen();
/// }
/// }
///
/// - Important: When called with [RevokeType.allSessions] the provided refresh JWT will not
/// be usable anymore and the user will need to sign in again.
Future<void> revokeSessions(RevokeType revokeType, String refreshJwt);

// Deprecated

/// Logs out from an active [DescopeSession].
@Deprecated('Use revokeSessions instead')
Future<void> logout(String refreshJwt);
}

Expand Down
16 changes: 15 additions & 1 deletion lib/src/types/others.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/// Which sessions to revoke when calling `DescopeAuth.revokeSessions()`
enum RevokeType {
/// Revokes the provided refresh JWT.
currentSession,
/// Revokes the provided refresh JWT and all other active sessions for the user.
///
/// - Important: This causes all sessions for the user to be removed, and the provided
/// refresh JWT will not be usable after the revokeSessions call completes.
allSessions,
}

/// The delivery method for an OTP or Magic Link message.
enum DeliveryMethod {
email,
Expand Down Expand Up @@ -75,7 +86,10 @@ class SignInOptions {
/// be nested under the `nsec` custom claim.
final Map<String, dynamic> customClaims;

const SignInOptions({this.stepupRefreshJwt, this.mfaRefreshJwt, this.customClaims = const {}});
/// Revokes all other active sessions for the user besides the new session being created.
final bool revokeOtherSessions;

const SignInOptions({this.stepupRefreshJwt, this.mfaRefreshJwt, this.customClaims = const {}, this.revokeOtherSessions = false});
}

/// Used to configure how users are updated.
Expand Down

0 comments on commit d5d664f

Please sign in to comment.