furaiev / amazon-cognito-identity-dart-2

Unofficial Amazon Cognito Identity Provider Dart SDK, to easily add user sign-up and sign-in to your mobile and web apps with AWS.
MIT License
187 stars 114 forks source link

CognitoUserPool.getCurrentUser() returns user with no session attached #82

Closed oysterpack closed 4 years ago

oysterpack commented 4 years ago

I would expect that if the user is authenticated, then the current user should be associated with a session. The current implementation never associates a session:

Future<CognitoUser> getCurrentUser() async {
    final lastUserKey =
        'CognitoIdentityServiceProvider.$_clientId.LastAuthUser';

    final lastAuthUser = await storage.getItem(lastUserKey);
    if (lastAuthUser != null) {
      return CognitoUser(lastAuthUser, this,
          storage: storage,
          clientSecret: _clientSecret,
          deviceName: _userAgent);
    }

    return null;
  }
furaiev commented 4 years ago

Correct, CognitoUserPool doesn't contain session while CognitoUser does.

oysterpack commented 4 years ago

I solved the problem ... in order to restore the session, tokens need to be cached, e.g.,

    final cognitoUser = CognitoUser(
      userEmail,
      userPool,
      storage: userPool.storage,
    );

    final authDetails = AuthenticationDetails(
      username: userEmail,
      password: password,
    );
    await cognitoUser.authenticateUser(authDetails);
    // in order to restore the session later on, the session tokens need to be cached into CognitoStorage
    await cognitoUser.cacheTokens(); 

    final currentUser = await userPool.getCurrentUser();
    final currentSession = await currentUser.getSession();
    expect(currentSession.isValid(), isTrue);
adrianjagielak commented 3 years ago

This should be documented or at least included in the example, I just lost hours debugging while the answer was just a single line of code.

pal commented 3 years ago

How come this is not default behaviour @furaiev? Do you know?

Seems like it would make a lot of sense to always persist/cache all persistable data if a Storage mechanism is in place?

adrianjagielak commented 3 years ago

Update from my experience from the latest non null safety version: this is only needed for non-web platforms, on web it precaches the tokens automatically (tested on web, macOS, and iOS).

furaiev commented 3 years ago

@pal this package is based on https://github.com/aws-amplify/amplify-js I'm not sure why this is not the default behavior in the official package.

nyck33 commented 3 years ago

I solved the problem ... in order to restore the session, tokens need to be cached, e.g.,

    final cognitoUser = CognitoUser(
      userEmail,
      userPool,
      storage: userPool.storage,
    );

    final authDetails = AuthenticationDetails(
      username: userEmail,
      password: password,
    );
    await cognitoUser.authenticateUser(authDetails);
    // in order to restore the session later on, the session tokens need to be cached into CognitoStorage
    await cognitoUser.cacheTokens(); 

    final currentUser = await userPool.getCurrentUser();
    final currentSession = await currentUser.getSession();
    expect(currentSession.isValid(), isTrue);

I tried caching on login in user_service then printing in user_service init like this:


  /// Login user
  Future<User?> login(String email, String password) async {
    _cognitoUser = CognitoUser(email, _userPool, storage: _userPool.storage);

    final authDetails = AuthenticationDetails(
      username: email,
      password: password,
    );

    bool isConfirmed;
    try {
      _session = await _cognitoUser?.authenticateUser(authDetails);
      //cache session tokens to persist
      //https://github.com/furaiev/amazon-cognito-identity-dart-2/issues/82#issuecomment-669139576
      await _cognitoUser?.cacheTokens();
//rest of code is unchanged
/// Initiate user session from local storage if present
  Future<bool> init() async {
    String keyPrefix =
        'CognitoIdentityServiceProvider.${_cognitoUser?.pool.getClientId()}?.${_cognitoUser?.username}';
    late final idTokenKey;
    late final accessTokenKey;
    late final refreshTokenKey;
    late final clockDriftKey;

    //call getKeys() new method for debug
    List<dynamic> allKeys = await _cognitoUser?.storage.getKeys();

    print('\n\nall keys: $allKeys\n\n');

    idTokenKey = await _cognitoUser?.storage.getItem('$keyPrefix.idToken');
    accessTokenKey =
        await _cognitoUser?.storage.getItem('$keyPrefix.accessToken');
    refreshTokenKey =
        await _cognitoUser?.storage.getItem('$keyPrefix.refreshToken');
    clockDriftKey =
        await _cognitoUser?.storage.getItem('$keyPrefix.clockDrift');

    print(
        'idTokenKey: $idTokenKey.toString\n\n accessTokenKey: $accessTokenKey\n\n refreshTokenKey: $refreshTokenKey\n\n, clockDriftKey: $clockDriftKey');

But it's printing null

I/flutter (11124): , clockDriftKey: null
I/flutter (11124): idTokenKey: null
I/flutter (11124): 
I/flutter (11124):  accessTokenKey: null
I/flutter (11124): 
I/flutter (11124):  refreshTokenKey: null
I/flutter (11124): 
I/flutter (11124): , clockDriftKey: null
I/flutter (11124): 

And you can see above I added an addKeys() method for cognito_storage.dart and all the child classes which leads to a hang on the Login... in the appbar.

abstract class CognitoStorage {
  Future<dynamic> setItem(String key, value);
  Future<dynamic> getItem(String key);
  Future<dynamic> removeItem(String key);
  Future<void> clear();
  Future<dynamic> getKeys();
}

class CognitoMemoryStorage extends CognitoStorage {
  @override
  Future<dynamic> setItem(String key, value) async {
    _dataMemory[key] = value;
    return _dataMemory[key];
  }

  @override
  Future<dynamic> getItem(String key) async {
    return _dataMemory[key];
  }

  @override
  Future<dynamic> removeItem(String key) async {
    return _dataMemory.remove(key);
  }

  @override
  Future<void> clear() async {
    _dataMemory = {};
  }

  //return keys
  @override
  Future<dynamic> getKeys() async {
    return _dataMemory.keys;
  }
}
nyck33 commented 3 years ago

I solved the problem ... in order to restore the session, tokens need to be cached, e.g.,

    final cognitoUser = CognitoUser(
      userEmail,
      userPool,
      storage: userPool.storage,
    );

    final authDetails = AuthenticationDetails(
      username: userEmail,
      password: password,
    );
    await cognitoUser.authenticateUser(authDetails);
    // in order to restore the session later on, the session tokens need to be cached into CognitoStorage
    await cognitoUser.cacheTokens(); 

    final currentUser = await userPool.getCurrentUser();
    final currentSession = await currentUser.getSession();
    expect(currentSession.isValid(), isTrue);

How do you retrieve the tokens?