aws / aws/aws-sdk-java-v2

Cognito: support for partial UpdateUserPoolClient calls

Open
#5,221 2 comments 0 reactions 0 assignees View on GitHub
feature-request p2 service-api service:cognito
Dominant language
Java
Stars
2.6k
Forks
1k
Avg merge
2d 9h
Merged PRs (30d)
51

Description

### Describe the feature

The `UpdateUserPoolClient` should support partial updates instead of having to provide every single parameter in the request.

### Use Case

Currently, the process to update a user pool client through the SDK is very cumbersome. For example, if we only want to update the `callbackURLs` on an existing user pool client, we have to pass every single property with the *existing* value to the `UpdateUserPoolClientRequest`. This is because Cognito [doesn't support partial updates](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_UpdateUserPoolClient.html):

> If you don't provide a value for an attribute, Amazon Cognito sets it to its default value.

```kotlin
import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient
import software.amazon.awssdk.services.cognitoidentityprovider.model.DescribeUserPoolClientRequest
import software.amazon.awssdk.services.cognitoidentityprovider.model.UpdateUserPoolClientRequest

class CognitoClient(
private val cognitoConfig: CognitoConfig,
private val cognitoClient: CognitoIdentityProviderClient,
) {
// redacted other methods

fun addCallbackUrlToAppClient(clientId: String, callbackUrl: String) {
val describeUserPoolClientRequest = DescribeUserPoolClientRequest.builder()
.clientId(clientId)
.userPoolId(cognitoConfig.userPoolId)
.build()

val existingUserPoolClient = cognitoClient
.describeUserPoolClient(describeUserPoolClientRequest)
.userPoolClient()

val existingCallbackUrls = existingUserPoolClient.callbackURLs()

if (existingCallbackUrls.contains(callbackUrl)) {
logger.info("Callback URL $callbackUrl already exists for client $clientId, skipping.")
return
}

// We need to copy the list because it's immutable.
val updatedCallbackUrls = existingCallbackUrls.toMutableList()
updatedCallbackUrls.add(callbackUrl)

/**
* From the Cognito docs: "WARNING - If you don't provide a value for an attribute,
* Amazon Cognito sets it to its default value."
*
* So here we are, setting all the attributes to their already existing values.
*/
val updateRequest = UpdateUserPoolClientRequest.builder()
.accessTokenValidity(existingUserPoolClient.accessTokenValidity())
.allowedOAuthFlows(existingUserPoolClient.allowedOAuthFlows())
.allowedOAuthFlowsUserPoolClient(existingUserPoolClient.allowedOAuthFlowsUserPoolClient())
.allowedOAuthScopes(existingUserPoolClient.allowedOAuthScopes())
.analyticsConfiguration(existingUserPoolClient.analyticsConfiguration())
.authSessionValidity(existingUserPoolClient.authSessionValidity())
// This is the only field we're _actually_ changing
.callbackURLs(updatedCallbackUrls)
.clientId(existingUserPoolClient.clientId())
.clientName(existingUserPoolClient.clientName())
.defaultRedirectURI(existingUserPoolClient.defaultRedirectURI())
.enablePropagateAdditionalUserContextData(existingUserPoolClient.enablePropagateAdditionalUserContextData())
.enableTokenRevocation(existingUserPoolClient.enableTokenRevocation())
.explicitAuthFlows(existingUserPoolClient.explicitAuthFlows())
.idTokenValidity(existingUserPoolClient.idTokenValidity())
.logoutURLs(existingUserPoolClient.logoutURLs())
.preventUserExistenceErrors(existingUserPoolClient.preventUserExistenceErrors())
.readAttributes(existingUserPoolClient.readAttributes())
.refreshTokenValidity(existingUserPoolClient.refreshTokenValidity())
.supportedIdentityProviders(existingUserPoolClient.supportedIdentityProviders())
.tokenValidityUnits(existingUserPoolClient.tokenValidityUnits())
.userPoolId(existingUserPoolClient.userPoolId())
.writeAttributes(existingUserPoolClient.writeAttributes())
.build()

cognitoClient.updateUserPoolClient(updateRequest)

logger.info("Successfully added callback URL $callbackUrl to Cognito App Client $clientId")
}
}
```

### Proposed Solution

This probably will need updates on the Cognito service side. However, I do think it would be nice if the SDK could provide a convenience method in the meantime. I guess something like:

```kotlin
import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient
import software.amazon.awssdk.services.cognitoidentityprovider.model.DescribeUserPoolClientRequest
import software.amazon.awssdk.services.cognitoidentityprovider.model.UpdateUserPoolClientRequest

class CognitoClient(
private val cognitoConfig: CognitoConfig,
private val cognitoClient: CognitoIdentityProviderClient,
) {
// redacted other methods

fun addCallbackUrlToAppClient(clientId: String, callbackUrl: String) {
val describeUserPoolClientRequest = DescribeUserPoolClientRequest.builder()
.clientId(clientId)
.userPoolId(cognitoConfig.userPoolId)
.build()

val existingUserPoolClient = cognitoClient
.describeUserPoolClient(describeUserPoolClientRequest)
.userPoolClient()

val existingCallbackUrls = existingUserPoolClient.callbackURLs()

if (existingCallbackUrls.contains(callbackUrl)) {
logger.info("Callback URL $callbackUrl already exists for client $clientId, skipping.")
return
}

// We need to copy the list because it's immutable.
val updatedCallbackUrls = existingCallbackUrls.toMutableList()
updatedCallbackUrls.add(callbackUrl)

val updateRequest = UpdateUserPoolClientRequest.builder()
.callbackURLs(updatedCallbackUrls)
.clientId(existingUserPoolClient.clientId())
.build()

cognitoClient.updateUserPoolClient(updateRequest, true)

logger.info("Successfully added callback URL $callbackUrl to Cognito App Client $clientId")
}
}
```

Note that `updateUserPoolClient` now takes a second parameter. This parameter could be named something like `keepExistingParameters` and default to `false`, so it's not a breaking change. If `true`, it could get the existing user pool (either by doing a `describeUserPoolClient` under the hood or require an existing `UserPoolClientType` to be passed).

### Other Information

Related Cognito docs: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_UpdateUserPoolClient.html

### Acknowledgements

- [ ] I may be able to implement this feature request
- [ ] This feature might incur a breaking change

### AWS Java SDK version used

2.25.50

### JDK version used

17.0.10

### Operating System and version

macOS 14.4.1

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.