Skip to content
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

Cognito Triggers - PreSignUp, PostConfirmation, PostAuthentication, CustomMessage #57

Merged
merged 6 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 213 additions & 22 deletions Sources/AWSLambdaEvents/Cognito.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,148 @@ enum CognitoEventError: Error, Sendable {
/// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
public enum CognitoEvent: Equatable, Sendable {
public struct CallerContext: Codable, Hashable, Sendable {
let awsSdkVersion: String
let clientId: String
/// The version of the AWS SDK that generated the request.
public let awsSdkVersion: String

/// The ID of the user pool app client.
public let clientId: String
}

public enum TriggerSource: String, Codable, Sendable {
sebsto marked this conversation as resolved.
Show resolved Hide resolved
case preSignUp_SignUp = "PreSignUp_SignUp"
case preSignUp_AdminCreateUser = "PreSignUp_AdminCreateUser"
case preSignUp_ExternalProvider = "PreSignUp_ExternalProvider"

case postConfirmation_ConfirmSignUp = "PostConfirmation_ConfirmSignUp"
case postConfirmation_ConfirmForgotPassword = "PostConfirmation_ConfirmForgotPassword"

case preAuthentication_Authentication = "PreAuthentication_Authentication"
case postAuthentication_Authentication = "PostAuthentication_Authentication"

case customMessage_SignUp = "CustomMessage_SignUp"
case customMessage_AdminCreateUser = "CustomMessage_AdminCreateUser"
case customMessage_ResendCode = "CustomMessage_ResendCode"
case customMessage_ForgotPassword = "CustomMessage_ForgotPassword"
case customMessage_UpdateUserAttribute = "CustomMessage_UpdateUserAttribute"
case customMessage_VerifyUserAttribute = "CustomMessage_VerifyUserAttribute"
case customMessage_Authentication = "CustomMessage_Authentication"

case defineAuthChallenge_Authentication = "DefineAuthChallenge_Authentication"
case createAuthChallenge_Authentication = "CreateAuthChallenge_Authentication"
case verifyAuthChallengeResponse_Authentication = "VerifyAuthChallengeResponse_Authentication"

case tokenGeneration_HostedAuth = "TokenGeneration_HostedAuth"
case tokenGeneration_Authentication = "TokenGeneration_Authentication"
case tokenGeneration_NewPasswordChallenge = "TokenGeneration_NewPasswordChallenge"
case tokenGeneration_AuthenticateDevice = "TokenGeneration_AuthenticateDevice"
case tokenGeneration_RefreshTokens = "TokenGeneration_RefreshTokens"

case userMigration_Authentication = "UserMigration_Authentication"
case userMigration_ForgotPassword = "UserMigration_ForgotPassword"
}

public struct Parameters: Codable, Equatable, Sendable {
let version: String
let triggerSource: String
let region: AWSRegion
let userPoolId: String
let userName: String
let callerContext: CallerContext
/// The version number of your Lambda function.
public let version: String

/// The name of the event that triggered the Lambda function.
///
/// For a description of each triggerSource see Connecting Lambda triggers to user pool functional operations. https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html#cognito-user-identity-pools-working-with-aws-lambda-trigger-sources
public let triggerSource: TriggerSource

/// The AWS Region as an AWSRegion instance.
public let region: AWSRegion

/// The ID of the user pool.
public let userPoolId: String

/// The current user's username.
public let userName: String

/// Metadata about the request and the code environment.
public let callerContext: CallerContext
}

case preSignUpSignUp(Parameters, PreSignUp)
case preSignUp(Parameters, PreSignUp)

public struct PreSignUp: Codable, Hashable, Sendable {
/// One or more name-value pairs representing user attributes. The attribute names are the keys.
public let userAttributes: [String: String]

/// One or more name-value pairs containing the validation data in the request to register a user.
///
/// The validation data is set and then passed from the client in the request to register a user. You can pass this data to your Lambda function by using the ClientMetadata parameter in the InitiateAuth and AdminInitiateAuth API actions.
public let validationData: [String: String]?

/// One or more key-value pairs that you can provide as custom input to the Lambda function that you specify for the pre sign-up trigger.
///
/// You can pass this data to your Lambda function by using the ClientMetadata parameter in the following API actions: AdminCreateUser, AdminRespondToAuthChallenge, ForgotPassword, and SignUp.
public let clientMetadata: [String: String]?
}

case postConfirmation(Parameters, PostConfirmation)

public struct PostConfirmation: Codable, Equatable, Sendable {
/// One or more key-value pairs representing user attributes.
public let userAttributes: [String: String]

/// One or more key-value pairs that you can provide as custom input to the Lambda function that you specify for the post confirmation trigger.
///
/// You can pass this data to your Lambda function by using the ClientMetadata parameter in the following API actions: AdminConfirmSignUp, ConfirmForgotPassword, ConfirmSignUp, and SignUp.
public let clientMetadata: [String: String]?
}

case postAuthentication(Parameters, PostAuthentication)

public struct PostAuthentication: Codable, Equatable, Sendable {
/// This flag indicates if the user has signed in on a new device. Amazon Cognito only sets this flag if the remembered devices value of the user pool is Always or User Opt-In.
public let newDeviceUsed: Bool

/// One or more name-value pairs representing user attributes.
public let userAttributes: [String: String]

/// One or more key-value pairs that you can provide as custom input to the Lambda function that you specify for the post authentication trigger.
///
/// To pass this data to your Lambda function, you can use the ClientMetadata parameter in the AdminRespondToAuthChallenge and RespondToAuthChallenge API actions.
/// Amazon Cognito doesn't include data from the ClientMetadata parameter in AdminInitiateAuth and InitiateAuth API operations in the request that it passes to the post authentication function.
public let clientMetadata: [String: String]?
}

case customMessage(Parameters, CustomMessage)

public struct CustomMessage: Codable, Equatable, Sendable {
/// A string for you to use as the placeholder for the verification code in the custom message.
public let codeParameter: String?

/// The user name. Amazon Cognito includes this parameter in requests that result from admin-created users.
public let usernameParameter: String?

/// One or more name-value pairs representing user attributes.
public let userAttributes: [String: String]

/// One or more key-value pairs that you can provide as custom input to the Lambda function that you specify for the custom message trigger.
///
/// The request that invokes a custom message function doesn't include data passed in the ClientMetadata parameter in AdminInitiateAuth and InitiateAuth API operations. To pass this data to your Lambda function, you can use the ClientMetadata parameter in the following API actions:
/// - AdminResetUserPassword
/// - AdminRespondToAuthChallenge
/// - AdminUpdateUserAttributes
/// - ForgotPassword
/// - GetUserAttributeVerificationCode
/// - ResendConfirmationCode
/// - SignUp
/// - UpdateUserAttributes
public let clientMetadata: [String: String]?
}

public var commonParameters: Parameters {
switch self {
case .preSignUpSignUp(let params, _):
case .preSignUp(let params, _):
return params
case .postConfirmation(let params, _):
return params
case .postAuthentication(let params, _):
return params
case .customMessage(let params, _):
return params
}
}
Expand All @@ -71,7 +182,7 @@ extension CognitoEvent: Codable {
let container = try decoder.container(keyedBy: CodingKeys.self)

let version = try container.decode(String.self, forKey: .version)
let triggerSource = try container.decode(String.self, forKey: .triggerSource)
let triggerSource = try container.decode(TriggerSource.self, forKey: .triggerSource)
let region = try container.decode(AWSRegion.self, forKey: .region)
let userPoolId = try container.decode(String.self, forKey: .userPoolId)
let userName = try container.decode(String.self, forKey: .userName)
Expand All @@ -80,12 +191,24 @@ extension CognitoEvent: Codable {
let params = CognitoEvent.Parameters(version: version, triggerSource: triggerSource, region: region, userPoolId: userPoolId, userName: userName, callerContext: callerContext)

switch triggerSource {
case "PreSignUp_SignUp":
case .preSignUp_SignUp, .preSignUp_ExternalProvider, .preSignUp_AdminCreateUser:
let value = try container.decode(CognitoEvent.PreSignUp.self, forKey: .request)
self = .preSignUp(params, value)

case .postConfirmation_ConfirmSignUp, .postConfirmation_ConfirmForgotPassword:
let value = try container.decode(CognitoEvent.PostConfirmation.self, forKey: .request)
self = .postConfirmation(params, value)

case .postAuthentication_Authentication:
let value = try container.decode(CognitoEvent.PostAuthentication.self, forKey: .request)
self = .postAuthentication(params, value)

case .customMessage_SignUp, .customMessage_AdminCreateUser, .customMessage_ResendCode, .customMessage_ForgotPassword, .customMessage_UpdateUserAttribute, .customMessage_VerifyUserAttribute, .customMessage_Authentication:
let value = try container.decode(CognitoEvent.CustomMessage.self, forKey: .request)
self = .customMessage(params, value)

self = .preSignUpSignUp(params, value)
default:
throw CognitoEventError.unimplementedEvent(triggerSource)
throw CognitoEventError.unimplementedEvent(triggerSource.rawValue)
}
}

Expand All @@ -102,14 +225,25 @@ extension CognitoEvent: Codable {
try container.encode(params.callerContext, forKey: .callerContext)

switch self {
case .preSignUpSignUp(_, let value):
case .preSignUp(_, let value):
try container.encode(value, forKey: .response)
case .postConfirmation(_, let value):
try container.encode(value, forKey: .response)
case .postAuthentication(_, let value):
try container.encode(value, forKey: .response)
case .customMessage(_, let value):
try container.encode(value, forKey: .response)
}
}
}

public enum CognitoEventResponse: Sendable {
case preSignUpSignUp(CognitoEvent.Parameters, CognitoEvent.PreSignUp, PreSignUp)
// Used for when there are no parameters expected in the response
public struct EmptyResponse: Codable, Equatable, Sendable {
public init() {}
}

case preSignUp(CognitoEvent.Parameters, CognitoEvent.PreSignUp, PreSignUp)

public struct PreSignUp: Codable, Hashable, Sendable {
public let autoConfirmUser: Bool
Expand All @@ -123,9 +257,38 @@ public enum CognitoEventResponse: Sendable {
}
}

case postConfirmation(CognitoEvent.Parameters, CognitoEvent.PostConfirmation, EmptyResponse)

case postAuthentication(CognitoEvent.Parameters, CognitoEvent.PostAuthentication, EmptyResponse)

case customMessage(CognitoEvent.Parameters, CognitoEvent.CustomMessage, CustomMessage)

public struct CustomMessage: Codable, Equatable, Sendable {
public init(smsMessage: String? = nil, emailMessage: String? = nil, emailSubject: String? = nil) {
self.smsMessage = smsMessage
self.emailMessage = emailMessage
self.emailSubject = emailSubject
}

/// The custom SMS message to be sent to your users. Must include the codeParameter value that you received in the request.
public let smsMessage: String?

/// The custom email message to send to your users. You can use HTML formatting in the emailMessage parameter. Must include the codeParameter value that you received in the request as the variable {####}.
public let emailMessage: String?

/// The subject line for the custom message
public let emailSubject: String?
}

public var commonParameters: CognitoEvent.Parameters {
switch self {
case .preSignUpSignUp(let params, _, _):
case .preSignUp(let params, _, _):
return params
case .postConfirmation(let params, _, _):
return params
case .postAuthentication(let params, _, _):
return params
case .customMessage(let params, _, _):
return params
}
}
Expand All @@ -147,7 +310,7 @@ extension CognitoEventResponse: Codable {
let container = try decoder.container(keyedBy: CodingKeys.self)

let version = try container.decode(String.self, forKey: .version)
let triggerSource = try container.decode(String.self, forKey: .triggerSource)
let triggerSource = try container.decode(CognitoEvent.TriggerSource.self, forKey: .triggerSource)
let region = try container.decode(AWSRegion.self, forKey: .region)
let userPoolId = try container.decode(String.self, forKey: .userPoolId)
let userName = try container.decode(String.self, forKey: .userName)
Expand All @@ -156,13 +319,32 @@ extension CognitoEventResponse: Codable {
let params = CognitoEvent.Parameters(version: version, triggerSource: triggerSource, region: region, userPoolId: userPoolId, userName: userName, callerContext: callerContext)

switch triggerSource {
case "PreSignUp_SignUp":
case .preSignUp_SignUp, .preSignUp_AdminCreateUser, .preSignUp_ExternalProvider:
let request = try container.decode(CognitoEvent.PreSignUp.self, forKey: .request)
let response = try container.decode(CognitoEventResponse.PreSignUp.self, forKey: .response)

self = .preSignUpSignUp(params, request, response)
self = .preSignUp(params, request, response)

case .postConfirmation_ConfirmSignUp, .postConfirmation_ConfirmForgotPassword:
let request = try container.decode(CognitoEvent.PostConfirmation.self, forKey: .request)
let response = try container.decode(CognitoEventResponse.EmptyResponse.self, forKey: .response)

self = .postConfirmation(params, request, response)

case .postAuthentication_Authentication:
let request = try container.decode(CognitoEvent.PostAuthentication.self, forKey: .request)
let response = try container.decode(CognitoEventResponse.EmptyResponse.self, forKey: .response)

self = .postAuthentication(params, request, response)

case .customMessage_SignUp, .customMessage_AdminCreateUser, .customMessage_ResendCode, .customMessage_ForgotPassword, .customMessage_UpdateUserAttribute, .customMessage_VerifyUserAttribute, .customMessage_Authentication:
let request = try container.decode(CognitoEvent.CustomMessage.self, forKey: .request)
let response = try container.decode(CognitoEventResponse.CustomMessage.self, forKey: .response)

self = .customMessage(params, request, response)

default:
throw CognitoEventError.unimplementedEvent(triggerSource)
throw CognitoEventError.unimplementedEvent(triggerSource.rawValue)
}
}

Expand All @@ -179,7 +361,16 @@ extension CognitoEventResponse: Codable {
try container.encode(params.callerContext, forKey: .callerContext)

switch self {
case .preSignUpSignUp(_, let request, let response):
case .preSignUp(_, let request, let response):
try container.encode(request, forKey: .request)
try container.encode(response, forKey: .response)
case .postConfirmation(_, let request, let response):
try container.encode(request, forKey: .request)
try container.encode(response, forKey: .response)
case .postAuthentication(_, let request, let response):
try container.encode(request, forKey: .request)
try container.encode(response, forKey: .response)
case .customMessage(_, let request, let response):
try container.encode(request, forKey: .request)
try container.encode(response, forKey: .response)
}
Expand Down
Loading