aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.31k stars 247 forks source link

Migrate to amplify_auth_cognito with credentials present #593

Open stsc3000 opened 3 years ago

stsc3000 commented 3 years ago

Hi! Is it possible to "silently" initiate a user session given a valid userId, accessToken, idToken and refreshToken, possibly also for federated (apple) sign in? We are exploring migrating to the official libs but do not necessarily want to prompt users to sign in again.

haverchuck commented 3 years ago

We do not currently have this feature, but I'm marking this as a request to get it into the backlog.

fpabl0 commented 2 years ago

Hello, we are developing an application that also needs this feature, as our application (apart from the normal cognito auth flow) uses other kind of login that is performed in a backend using the cognito admin sdk that returns the cognito access tokens.

amplify-js has a function called setSignInUserSession that allow to solve this use case, however amplify-flutter does not have it. I have opened issues in amplify-android and amplify-ios that should be solved in order implement this feature in flutter.

LudoPL commented 1 year ago

Hi ! I wouldn't recommand to do it, but for info, in the version 1.0.0-next.7 (which is recommended not to use in production), it is possible to implement our own "SecureStorageInterface" await Amplify.addPlugin(AmplifyAuthCognito(credentialStorage: AmplifyMigrationStorage()). That means in it is possible to have a custom implementation that inherits of AmplifySecureStorage except at the initialization, it will retrieve the token from the previous authentication lib, and write them in the storage of the amplify lib:

        await write(key: 'version', value: 'v1');
        await write(key: '$cognitoClientId.authFlowType', value: 'USER_SRP_AUTH');
        await write(key: '$cognitoClientId.accessToken', value: accessToken);
        await write(key: '$cognitoClientId.refreshToken', value: refreshToken);
        await write(key: '$cognitoClientId.idToken', value: idToken);
        await write(key: '$cognitoClientId.username', value: username);

This should only be done once on the first launch after the migration.

Note that in next version 1.0.0-next.8 it has already change and not possible to do it exactly like that any-more, but if your update to that version after the user have been migrated, they will stay connected.

Jordan-Nelson commented 1 year ago

Version 1.0 still allows for a custom SecureStorage impl. The API did change during developer preview though. You can see the docs for an example of how to implement your own version of secure storage. See: https://docs.amplify.aws/lib/auth/managing_credentials/q/platform/flutter/

This is NOT intended to be used to migrate with existing credentials. The purpose of allowing consumers to implement a custom SecureStorage impl is to change where sensitive data is persisted. The docs show an example of how to use an in memory implementation to avoid persisting credentials.

The keys used to store credentials could change in the future, so implementations should not depend on specific key names or any other internal details.

LudoPL commented 1 year ago

If I am not mistaken, what has changed (justified for security reason I guess) in the next.8 (and so probably in the 1.0), is the ability to easily inherit of AmplifySecureStorage, which is helpful to avoid changing how the credentials are store by default by amplify (using device secure storage), and guarantee that once the migration is done, it works exactly like it would have without the migration code. Once most of your user sessions have been migrated, you can remove the migration code, and so if key names or other internal details change, you won't have more issue than other user of the lib have and I guess the amplify team will handle the migration of the old storage to the new one. But of course the migration code it self won't be compatible with the new implementation, so if some users skip several version of your app, they will loose the cognito session when migrating from the version that use your old cognito lib (not amplify) to the version that use the new amplify lib that doesn't exist yet and is not compatible with the amplify version specified in your dependencies. Of course it is just a idea for a workaround, it would be a lot better to be able to do that with an official way.

Jordan-Nelson commented 1 year ago

While I agree it is theoretically possible, I highly recommend against using this to migrate credentials as it would require relying on the internal implementation details of Auth (particularly the key names), and those implementation details could change at any point as they are not part of the public API.

I also highly recommend against using any preview (*.next.x) version of amplify in production. We have released 1.0.0 which is stable. There are known breaking changes between some of the preview versions and the first stable version. Some of those breaking changes include changes in how credentials are stored, and there is no credential migration guarantees between preview versions. Amplify Auth will automatically migrate credentials between all stable versions. For example, from v0.6.x to v1.0.0.

The changes around AmplifySecureStorage in next.8 (and later 1.0.0) had a few purposes

  1. The API was updated so that consumers could change platform specific options without having to worry about what "scope" to use. The default constructor (which was marked as internal in starting in next.8) required setting a "scope". The "scope" is essentially a name space for the plugin and should never change, so it didn't make sense to force consumers to set it (or really to even allow it to be changed).
  2. The name was changed the name from credentialStorage to secureStorageFactory to better reflect that more than just credentials will be stored.
  3. credentialStorage expected a instance, whereas secureStorageFactory expects a factory. This allows for the Auth plugin to use the provided factory to create multiple instances (potentially with different scopes). Although currently only one instance will be created today.
// old api

await Amplify.addPlugin(
  AmplifyAuthCognito(
    credentialStorage: AmplifySecureStorage(
      scope: 'awsCognitoAuthPlugin', // <- this was a required argument, and if it ever changed, data would be lost.
      webOptions: WebSecureStorageOptions(
        persistenceOption: WebPersistenceOption.inMemory,
      ),
    ),
  ),
);
// current api - no scope required.

await Amplify.addPlugin(
  AmplifyAuthCognito(
    secureStorageFactory: AmplifySecureStorage.factoryFrom(
      webOptions: WebSecureStorageOptions(
        persistenceOption: WebPersistenceOption.inMemory,
      ),
    ),
  ),
);
LudoPL commented 1 year ago

Thanks for the explanation and all the details. As I said, I neither recommand to do this but at the same time I haven't seen a better workaround to allow to migrate to the amplify lib without disconnecting all the users. I mean of course, one way could be to fork the lib / propose a pull request to implement the feature correctly. Do you know if there have been change in the way credentials are stored between 1.0.0-next.7 and the 1.0.0 on mobile devices (key names) ? (I have tested with success migration from next-7 to next-8 but not from next-8 to 1.0.0). [edit]The migration from next-7 to 1.0.0 seems ok, so once most of the user session have been migrated to the new amplify lib, we will be able to remove the migration code and go to the 1.0 with a clean state.[/edit]

Jordan-Nelson commented 1 year ago

Do you know if there have been change in the way credentials are stored between 1.0.0-next.7 and the 1.0.0 on mobile devices (key names)

I don't believe so

r3rer3 commented 10 months ago

Any updates on this one? Right now, I am doing a kinda of a hack: calling Amplify.reset() and then addPlugin and configure.