-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GEN 2][Mobile] Move the Accessing credentials" page for Flutter, iOS and Android #7207
Merged
josefaidt
merged 6 commits into
aws-amplify:next-release/main
from
salihgueler:mobile/auth/accessingcredentials
Apr 24, 2024
Merged
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
8285853
Move the accessing credentials code for Flutter, iOS and Android to G…
salihgueler 7503ae7
Update the name and add managing credentials.
salihgueler 80b1ccc
Apply suggestions from code review
salihgueler 2f0d6fd
Merge branch 'next-release/main' into mobile/auth/accessingcredentials
salihgueler 645b66c
Add the missing paragraphs.
salihgueler fc74b37
Update src/pages/[platform]/build-a-backend/auth/accessing-credential…
salihgueler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
242 changes: 242 additions & 0 deletions
242
src/pages/[platform]/build-a-backend/auth/accessing-credentials/index.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; | ||
|
||
export const meta = { | ||
title: 'Accessing Credentials', | ||
description: 'Use AWS Cognito Auth plugin to access auth credentials with Amplify Gen 2', | ||
platforms: [ | ||
'android', | ||
'flutter', | ||
'swift', | ||
] | ||
}; | ||
|
||
export const getStaticPaths = async () => { | ||
return getCustomStaticPath(meta.platforms); | ||
}; | ||
|
||
export function getStaticProps(context) { | ||
return { | ||
props: { | ||
platform: context.params.platform, | ||
meta | ||
} | ||
}; | ||
} | ||
|
||
<InlineFilter filters={['flutter']}> | ||
However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation details by calling fetchAuthSession on the Cognito Auth Plugin. This will return a `CognitoAuthSession`, which has additional attributes compared to `AuthSession`, which is typically returned by fetchAuthSession. See the example below: | ||
|
||
```dart | ||
Future<void> fetchAuthSession() async { | ||
try { | ||
final result = await Amplify.Auth.fetchAuthSession(); | ||
safePrint('User is signed in: ${result.isSignedIn}'); | ||
} on AuthException catch (e) { | ||
safePrint('Error retrieving auth session: ${e.message}'); | ||
} | ||
} | ||
``` | ||
|
||
### Retrieving AWS credentials | ||
|
||
Sometimes it can be helpful to retrieve the instance of the underlying plugin | ||
which has more specific typing. In the case of Cognito, calling `fetchAuthSession` | ||
on the Cognito plugin returns AWS-specific values such as the identity ID, | ||
AWS credentials, and Cognito User Pool tokens. | ||
salihgueler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```dart | ||
Future<void> fetchCognitoAuthSession() async { | ||
try { | ||
final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); | ||
final result = await cognitoPlugin.fetchAuthSession(); | ||
final identityId = result.identityIdResult.value; | ||
safePrint("Current user's identity ID: $identityId"); | ||
} on AuthException catch (e) { | ||
safePrint('Error retrieving auth session: ${e.message}'); | ||
} | ||
} | ||
``` | ||
</InlineFilter> | ||
<InlineFilter filters={['android']}> | ||
However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation details by casting the result of fetchAuthSession as follows: | ||
|
||
<BlockSwitcher> | ||
<Block name="Java"> | ||
|
||
```java | ||
Amplify.Auth.fetchAuthSession( | ||
result -> { | ||
AWSCognitoAuthSession cognitoAuthSession = (AWSCognitoAuthSession) result; | ||
switch(cognitoAuthSession.getIdentityIdResult().getType()) { | ||
case SUCCESS: | ||
Log.i("AuthQuickStart", "IdentityId: " + cognitoAuthSession.getIdentityIdResult().getValue()); | ||
break; | ||
case FAILURE: | ||
Log.i("AuthQuickStart", "IdentityId not present because: " + cognitoAuthSession.getIdentityIdResult().getError().toString()); | ||
} | ||
}, | ||
error -> Log.e("AuthQuickStart", error.toString()) | ||
); | ||
``` | ||
|
||
</Block> | ||
<Block name="Kotlin - Callbacks"> | ||
|
||
```kotlin | ||
Amplify.Auth.fetchAuthSession( | ||
{ | ||
val session = it as AWSCognitoAuthSession | ||
when (session.identityIdResult.type) { | ||
AuthSessionResult.Type.SUCCESS -> | ||
Log.i("AuthQuickStart", "IdentityId = ${session.identityIdResult.value}") | ||
AuthSessionResult.Type.FAILURE -> | ||
Log.w("AuthQuickStart", "IdentityId not found", session.identityIdResult.error) | ||
} | ||
}, | ||
{ Log.e("AuthQuickStart", "Failed to fetch session", it) } | ||
) | ||
``` | ||
|
||
</Block> | ||
<Block name="Kotlin - Coroutines"> | ||
|
||
```kotlin | ||
try { | ||
val session = Amplify.Auth.fetchAuthSession() as AWSCognitoAuthSession | ||
val id = session.identityIdResult | ||
if (id.type == AuthSessionResult.Type.SUCCESS) { | ||
Log.i("AuthQuickStart", "IdentityId: ${id.value}") | ||
} else if (id.type == AuthSessionResult.Type.FAILURE) { | ||
Log.i("AuthQuickStart", "IdentityId not present: ${id.error}") | ||
} | ||
} catch (error: AuthException) { | ||
Log.e("AuthQuickStart", "Failed to fetch session", error) | ||
} | ||
``` | ||
|
||
</Block> | ||
<Block name="RxJava"> | ||
|
||
```java | ||
RxAmplify.Auth.fetchAuthSession() | ||
.subscribe( | ||
result -> { | ||
AWSCognitoAuthSession cognitoAuthSession = (AWSCognitoAuthSession) result; | ||
|
||
switch (cognitoAuthSession.getIdentityIdResult().getType()) { | ||
case SUCCESS: | ||
Log.i("AuthQuickStart", "IdentityId: " + cognitoAuthSession.getIdentityIdResult().getValue()); | ||
break; | ||
case FAILURE: | ||
Log.i("AuthQuickStart", "IdentityId not present because: " + cognitoAuthSession.getIdentityIdResult().getError().toString()); | ||
} | ||
}, | ||
error -> Log.e("AuthQuickStart", error.toString()) | ||
); | ||
``` | ||
|
||
</Block> | ||
</BlockSwitcher> | ||
|
||
## Force refreshing session | ||
|
||
You can ask the plugin to force refresh the internal session by setting the `forceRefresh` option when calling the fetchAuthSession API. | ||
|
||
<BlockSwitcher> | ||
<Block name="Java"> | ||
|
||
```java | ||
AuthFetchSessionOptions options = AuthFetchSessionOptions.builder().forceRefresh(true).build(); | ||
``` | ||
|
||
</Block> | ||
<Block name="Kotlin - Callbacks"> | ||
|
||
```kotlin | ||
val option = AuthFetchSessionOptions.builder().forceRefresh(true).build() | ||
``` | ||
|
||
</Block> | ||
<Block name="Kotlin - Coroutines"> | ||
|
||
```kotlin | ||
val option = AuthFetchSessionOptions.builder().forceRefresh(true).build() | ||
``` | ||
|
||
</Block> | ||
<Block name="RxJava"> | ||
|
||
```java | ||
AuthFetchSessionOptions options = AuthFetchSessionOptions.builder().forceRefresh(true).build(); | ||
``` | ||
|
||
</Block> | ||
</BlockSwitcher> | ||
</InlineFilter> | ||
<InlineFilter filters={['swift']}> | ||
However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation details by casting the result of fetchAuthSession as follows: | ||
|
||
```swift | ||
import AWSPluginsCore | ||
|
||
do { | ||
let session = try await Amplify.Auth.fetchAuthSession() | ||
|
||
// Get user sub or identity id | ||
if let identityProvider = session as? AuthCognitoIdentityProvider { | ||
let usersub = try identityProvider.getUserSub().get() | ||
let identityId = try identityProvider.getIdentityId().get() | ||
print("User sub - \(usersub) and identity id \(identityId)") | ||
} | ||
|
||
// Get AWS credentials | ||
if let awsCredentialsProvider = session as? AuthAWSCredentialsProvider { | ||
let credentials = try awsCredentialsProvider.getAWSCredentials().get() | ||
// Do something with the credentials | ||
} | ||
|
||
// Get cognito user pool token | ||
if let cognitoTokenProvider = session as? AuthCognitoTokensProvider { | ||
let tokens = try cognitoTokenProvider.getCognitoTokens().get() | ||
// Do something with the JWT tokens | ||
} | ||
} catch let error as AuthError { | ||
print("Fetch auth session failed with error - \(error)") | ||
} catch { | ||
} | ||
``` | ||
If you have enabled guest user in Cognito Identity Pool and no user is signed in, you will be able to access only identityId and AWS credentials. All other session details will give you an error. | ||
|
||
```swift | ||
import AWSPluginsCore | ||
|
||
do { | ||
let session = try await Amplify.Auth.fetchAuthSession() | ||
|
||
// Get identity id | ||
if let identityProvider = session as? AuthCognitoIdentityProvider { | ||
let identityId = try identityProvider.getIdentityId().get() | ||
print("Identity id \(identityId)") | ||
} | ||
|
||
// Get AWS credentials | ||
if let awsCredentialsProvider = session as? AuthAWSCredentialsProvider { | ||
let credentials = try awsCredentialsProvider.getAWSCredentials().get() | ||
// Do something with the credentials | ||
} | ||
} catch let error as AuthError { | ||
print("Fetch auth session failed with error - \(error)") | ||
} catch { | ||
print("Unexpected error: \(error)") | ||
} | ||
``` | ||
|
||
## Force refreshing session | ||
|
||
You can ask the plugin to force refresh the internal session by passing an api options `forceRefresh` while calling the fetchAuthSession api. | ||
|
||
```swift | ||
Amplify.Auth.fetchAuthSession(options: .forceRefresh()) | ||
|
||
``` | ||
</InlineFilter> |
86 changes: 86 additions & 0 deletions
86
src/pages/[platform]/build-a-backend/auth/managing-credentials/index.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; | ||
|
||
export const meta = { | ||
title: 'Manage credentials', | ||
description: | ||
'Learn how to manage auth credentials for apps with Gen 2.', | ||
platforms: [ | ||
'flutter', | ||
] | ||
}; | ||
|
||
export const getStaticPaths = async () => { | ||
return getCustomStaticPath(meta.platforms); | ||
}; | ||
|
||
export function getStaticProps(context) { | ||
return { | ||
props: { | ||
platform: context.params.platform, | ||
meta | ||
} | ||
}; | ||
} | ||
|
||
<InlineFilter filters={['flutter']}> | ||
|
||
The Amplify Auth category persists authentication-related information to make it available to other Amplify categories and to your application. | ||
|
||
Amplify Flutter securely manages credentials and user identity information. You do not need to store, refresh, or delete credentials yourself. Amplify Flutter stores auth data on the device using platform capabilities such as [Keychain Services](https://developer.apple.com/documentation/security/keychain_services/) on iOS and macOS and [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences) on Android. | ||
|
||
<Callout info={true} warning={false}> | ||
salihgueler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Amplify will refresh the [Access Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html) and [ID Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html) as long as the [Refresh Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html) is valid. Once the Refresh token expires, the user will need to reauthenticate to obtain a new one. | ||
salihgueler marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
</Callout> | ||
|
||
Some platform specific option can be customized with the out of the box options. In the example below, credentials will be stored in-memory on Web instead of the default behavior of using browser storage. | ||
|
||
```dart | ||
await Amplify.addPlugin( | ||
AmplifyAuthCognito( | ||
secureStorageFactory: AmplifySecureStorage.factoryFrom( | ||
webOptions: WebSecureStorageOptions( | ||
persistenceOption: WebPersistenceOption.inMemory, | ||
), | ||
), | ||
), | ||
); | ||
``` | ||
|
||
If you would like further customization, you can provide your own factory for creating `SecureStorageInterface` instances to `AmplifyAuthCognito`. The example below shows the use of a custom implementation that stores data in-memory on all platforms. | ||
|
||
```dart | ||
await Amplify.addPlugin( | ||
AmplifyAuthCognito(secureStorageFactory: InMemoryStorage.new), | ||
); | ||
``` | ||
|
||
```dart | ||
class InMemoryStorage implements SecureStorageInterface { | ||
InMemoryStorage(this.scope); | ||
|
||
/// The scope of the item being stored. | ||
/// | ||
/// This can be used as a namespace for stored items. | ||
final AmplifySecureStorageScope scope; | ||
|
||
static final Map<String, String> _data = {}; | ||
|
||
@override | ||
void write({required String key, required String value}) { | ||
_data['${scope.name}.$key'] = value; | ||
} | ||
|
||
@override | ||
String? read({required String key}) { | ||
return _data['${scope.name}.$key']; | ||
} | ||
|
||
@override | ||
void delete({required String key}) { | ||
_data.remove('${scope.name}.$key'); | ||
} | ||
} | ||
``` | ||
</InlineFilter> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this reads like there is some missing context