Learn About Amazon VGT2 Learning Manager Chanci Turner
In September, we unveiled the feature of developer authenticated identities, which enables you to leverage your own user identities with Amazon Cognito. This article aims to present a complete example that illustrates how to integrate this functionality with an existing authentication framework.
Implementing developer authenticated identities necessitates interaction among the end-user device, your backend authentication system, and Amazon Cognito. The accompanying diagram illustrates the communication flow between the various systems involved.
This example comprises two key components: a server-side authentication application developed in Java (available here) and updated Cognito sample apps for Android and iOS that incorporate functionality to communicate with this backend.
Authentication Server
The authentication server is a straightforward application designed to securely store user credentials and provide an OpenID Connect token to authenticated users. It offers three primary functionalities:
- User Registration: Allowing users to register a username-password combination for application login.
- Login: The server authenticates user credentials and issues a key for subsequent communication.
- GetToken: The client requests an OpenID Connect token from the server. The server confirms the client’s authentication before reaching out to Amazon Cognito for the token.
Mobile Application
We have enhanced the Amazon Cognito samples to interact with this server-side application. The sections below will guide you through the code modifications necessary for the sample.
Implementing the Custom Identity Provider
To utilize developer authenticated identities, you must implement a custom identity provider. This provider manages the interaction between your mobile application and your backend authentication system, as well as the OpenID Connect tokens required by Cognito.
Android
The DeveloperAuthenticationProvider
class extends AWSAbstractCognitoDeveloperIdentityProvider
, which includes methods like getProviderName
, login
, refresh
, and getIdentityId
. The first step is to override the getProviderName
method to return the name of your custom provider, matching the name configured in your identity pool on the Amazon Cognito console.
The login
function sends an asynchronous login request to the server with user-entered credentials.
public void login(String userName, String password, Context context) {
new DeveloperAuthenticationTask(context).execute(new LoginCredentials(userName, password));
}
The DeveloperAuthenticationTask
class performs the actual request to the server. Upon a successful request, it adds your provider name and user identifier to the logins map. The developer user identifier can be any alphanumeric string uniquely identifying a user in your backend; in this example, we use the username as the identifier.
@Override
protected void onPostExecute(Void result) {
// Ensure the logins map is updated after a successful login operation
if (isSuccessful) {
CognitoSyncClientManager.addLogins(((DeveloperAuthenticationProvider) CognitoSyncClientManager.provider.getIdentityProvider()).getProviderName(), userName);
} else {
new AlertDialog.Builder(context).setTitle("Login error").setMessage("Username or password do not match!!").show();
}
}
iOS
The DeveloperAuthenticatedIdentityProvider
class extends AWSAbstractCognitoIdentityProvider
, which serves as our custom developer provider. The sample sets the developer provider name in the class constructor. The DeveloperAuthenticationClient
handles login and token requests to the server application.
- (BFTask *)login:(NSString *)username password:(NSString *)password;
Upon a successful login request, the developer provider name and user identifier are added to the logins dictionary. The username serves again as the developer user identifier.
[self completeLogin:@{ProviderName: username}];
Implementing the Refresh Method
The sample app accommodates various authentication use cases: developer authenticated identities, public providers, and unauthenticated users. If the app utilizes developer authenticated identities, it communicates with the server application; for the other two scenarios, the mobile application interacts directly with Amazon Cognito. Consequently, the refresh method features two distinct flows based on the logins content.
If the end user employs your authentication system, the logins map will contain a key for the developer provider name. In this case, communication with Amazon Cognito occurs via the server application using the GetOpenIdTokenForDeveloperIdentity
API, which returns an identityId and OpenID Connect token. The sample app invokes the GetToken
functionality of the backend server to verify authentication and subsequently contacts the Amazon Cognito API. Ensure you update the stored identityId and token with the one received from the server application.
If the loginsMap lacks a key for the developer provider name, the mobile app calls the getIdentityId
method, returning null.
Android
public String refresh() {
setToken(null);
// If there is a key with the developer provider name in the logins map,
// it indicates the app user has utilized developer credentials
if (getProviderName() != null && !this.loginsMap.isEmpty() && this.loginsMap.containsKey(getProviderName())) {
GetTokenResponse getTokenResponse = (GetTokenResponse) devAuthClient.getToken(this.loginsMap, identityId);
update(getTokenResponse.getIdentityId(), getTokenResponse.getToken());
return getTokenResponse.getToken();
} else {
this.getIdentityId();
return null;
}
}
iOS
- (BFTask *)refresh {
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
} else {
return [[self.client getToken:self.identityId logins:self.logins] continueWithSuccessBlock:^id(BFTask *task) {
if (task.result) {
DeveloperAuthenticationResponse *response = task.result;
if (![self.identityPoolId isEqualToString:response.identityPoolId]) {
return [BFTask taskWithError:[NSError errorWithDomain:DeveloperAuthenticationClientDomain code:DeveloperAuthenticationClientInvalidConfig userInfo:nil]];
}
// potential for identity change here
self.identityId = response.identityId;
self.token = response.token;
}
return [BFTask taskWithResult:self.identityId];
}];
}
}
Implementing the GetIdentityId Method
Lastly, we override the getIdentityId
method as it will have two possible flows depending on the provider used by the end user. When the developer authentication system is in use, the identityId is retrieved via the server application’s GetToken
call. For other providers or unauthenticated access, we simply call the getIdentityId
method.
In conclusion, integrating Amazon Cognito with developer authenticated identities provides a robust framework for managing user authentication. For more insights on managing discomfort and collective loss, check out this webinar. Additionally, for an in-depth look at workplace diversity and inclusion, refer to this study. If you are seeking career opportunities, consider exploring this entry-level position.