diff --git a/CHANGELOG.md b/CHANGELOG.md index 57da870..9f48e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.2.0 - 2021-08-12 +### Added +* Added ```ChangePassword```, ```ForgotPassword``` and ```ForgotPasswordConfirmation``` functions. + +### Changed +* **BREAKING**: Changed the return type of the ```SignIn``` function from ```*types.AuthenticationResultType``` to ```*cognitoidentityprovider.InitiateAuthOutput```. +* **BREAKING**: Renamed ```CognitoUserPoolClientID``` to ```UserPoolClient```. +* **BREAKING**: Removed ```passwordConfirmation``` parameter from ```SignUp``` + ## v0.1.0 - 2021-08-11 ### Added * Added a changelog. diff --git a/auth.go b/auth.go index 84db4a8..4764781 100644 --- a/auth.go +++ b/auth.go @@ -15,8 +15,8 @@ var ( CognitoClient *cognitoidentityprovider.Client // CognitoUserPoolID is the id of the user pool in Cognito. CognitoUserPoolID string - // CognitoUserPoolClientID is the id of the user pool client in Cognito. - CognitoUserPoolClientID string + // CognitoClientID is the id of the user pool client in Cognito. + CognitoClientID string ) // Initialize will initialize the auth package. Both profile and region parameters are option if authentication can be @@ -36,7 +36,7 @@ func Initialize(ctx context.Context, profile string, region string, cognitoUserP CognitoClient = cognitoidentityprovider.NewFromConfig(cfg) CognitoUserPoolID = cognitoUserPoolID - CognitoUserPoolClientID = cognitoClientID + CognitoClientID = cognitoClientID return nil } @@ -46,7 +46,7 @@ func checkPackage() error { return xerror.New("db.CognitoClient is nil, have you called auth.Initialize()?") } - if CognitoUserPoolClientID == "" { + if CognitoClientID == "" { return xerror.New("db.CognitoClientID is empty, did you call auth.Initialize()?") } diff --git a/auth_test.go b/auth_test.go index 15aef63..7f1efcc 100644 --- a/auth_test.go +++ b/auth_test.go @@ -32,14 +32,14 @@ func setup(t *testing.T) { // Create the user pool and set the id for the auth package. userPoolOutput, err := auth.CognitoClient.CreateUserPool(context.Background(), &cognitoidentityprovider.CreateUserPoolInput{ - PoolName: aws.String(fmt.Sprintf("waste-scanner-test-user-pool_%d", time.Now().UnixNano())), + PoolName: aws.String(fmt.Sprintf("aws-auth-test-user-pool_%d", time.Now().UnixNano())), }) require.NoError(t, err) auth.CognitoUserPoolID = *userPoolOutput.UserPool.Id // Create the user pool client and set the id for the auth package. userPoolClientOutput, err := auth.CognitoClient.CreateUserPoolClient(context.Background(), &cognitoidentityprovider.CreateUserPoolClientInput{ - ClientName: aws.String(fmt.Sprintf("waste-scanner-test-user-pool-client_%d", time.Now().UnixNano())), + ClientName: aws.String(fmt.Sprintf("aws-auth-test-user-pool-client_%d", time.Now().UnixNano())), UserPoolId: aws.String(auth.CognitoUserPoolID), ExplicitAuthFlows: []types.ExplicitAuthFlowsType{ types.ExplicitAuthFlowsTypeAllowUserPasswordAuth, @@ -47,7 +47,7 @@ func setup(t *testing.T) { }, }) require.NoError(t, err) - auth.CognitoUserPoolClientID = *userPoolClientOutput.UserPoolClient.ClientId + auth.CognitoClientID = *userPoolClientOutput.UserPoolClient.ClientId } func teardown(t *testing.T) { diff --git a/change_password.go b/change_password.go new file mode 100644 index 0000000..a5e8234 --- /dev/null +++ b/change_password.go @@ -0,0 +1,26 @@ +package auth + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" + "github.com/gofor-little/xerror" +) + +// ChangePassword changes a user's password. +func ChangePassword(ctx context.Context, accessToken string, oldPassword string, newPassword string) error { + if err := checkPackage(); err != nil { + return xerror.Wrap("checkPackage call failed", err) + } + + if _, err := CognitoClient.ChangePassword(ctx, &cognitoidentityprovider.ChangePasswordInput{ + AccessToken: aws.String(accessToken), + PreviousPassword: aws.String(oldPassword), + ProposedPassword: aws.String(newPassword), + }); err != nil { + return xerror.Wrap("failed to change password", err) + } + + return nil +} diff --git a/change_password_test.go b/change_password_test.go new file mode 100644 index 0000000..7e625ff --- /dev/null +++ b/change_password_test.go @@ -0,0 +1,44 @@ +package auth_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" + "github.com/stretchr/testify/require" + + auth "github.com/gofor-little/aws-auth" +) + +func TestChangePassword(t *testing.T) { + setup(t) + defer teardown(t) + + testCases := []struct { + emailAddress string + password string + }{ + {"john@example.com", "test-Password1234!!"}, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("TestChangePassword%d", i), func(t *testing.T) { + _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password) + require.NoError(t, err) + + // Confirm the user without the use of a confirmation code so we can test the sign in. + _, err = auth.CognitoClient.AdminConfirmSignUp(context.Background(), &cognitoidentityprovider.AdminConfirmSignUpInput{ + UserPoolId: aws.String(auth.CognitoUserPoolID), + Username: aws.String(tc.emailAddress), + }) + require.NoError(t, err) + + output, err := auth.SignIn(context.Background(), tc.emailAddress, tc.password) + require.NoError(t, err) + + require.NoError(t, auth.ChangePassword(context.Background(), *output.AuthenticationResult.AccessToken, tc.password, fmt.Sprintf("new-%s", tc.password))) + }) + } +} diff --git a/forgot_password.go b/forgot_password.go new file mode 100644 index 0000000..9d2eae6 --- /dev/null +++ b/forgot_password.go @@ -0,0 +1,36 @@ +package auth + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" + "github.com/gofor-little/xerror" +) + +// ForgotPassword will initiate a forgot password request. +func ForgotPassword(ctx context.Context, emailAddress string) (*cognitoidentityprovider.ForgotPasswordOutput, error) { + output, err := CognitoClient.ForgotPassword(ctx, &cognitoidentityprovider.ForgotPasswordInput{ + ClientId: aws.String(CognitoClientID), + Username: aws.String(emailAddress), + }) + if err != nil { + return nil, xerror.Wrap("failed to send forgot password request", err) + } + + return output, nil +} + +// ForgotPasswordConfirm will confirm a forgot password request. +func ForgotPasswordConfirm(ctx context.Context, confirmationCode string, emailAddress string, newPassword string) error { + if _, err := CognitoClient.ConfirmForgotPassword(ctx, &cognitoidentityprovider.ConfirmForgotPasswordInput{ + ClientId: aws.String(CognitoClientID), + ConfirmationCode: aws.String(confirmationCode), + Password: aws.String(newPassword), + Username: aws.String(emailAddress), + }); err != nil { + return xerror.Wrap("failed to send forgot password confirmation request", err) + } + + return nil +} diff --git a/forgot_password_test.go b/forgot_password_test.go new file mode 100644 index 0000000..79e2bed --- /dev/null +++ b/forgot_password_test.go @@ -0,0 +1,56 @@ +package auth_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types" + "github.com/stretchr/testify/require" + + auth "github.com/gofor-little/aws-auth" +) + +func TestForgotPassword(t *testing.T) { + setup(t) + defer teardown(t) + + testCases := []struct { + emailAddress string + password string + }{ + {"john@example.com", "test-Password1234!!"}, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("TestForgotPassword%d", i), func(t *testing.T) { + _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password) + require.NoError(t, err) + + // Confirm the user without the use of a confirmation code so we can test the sign in. + _, err = auth.CognitoClient.AdminConfirmSignUp(context.Background(), &cognitoidentityprovider.AdminConfirmSignUpInput{ + UserPoolId: aws.String(auth.CognitoUserPoolID), + Username: aws.String(tc.emailAddress), + }) + require.NoError(t, err) + + // Set the email verified to true so we can use it in the forgot password request. + _, err = auth.CognitoClient.AdminUpdateUserAttributes(context.Background(), &cognitoidentityprovider.AdminUpdateUserAttributesInput{ + UserAttributes: []types.AttributeType{ + { + Name: aws.String("email_verified"), + Value: aws.String("true"), + }, + }, + UserPoolId: aws.String(auth.CognitoUserPoolID), + Username: aws.String(tc.emailAddress), + }) + require.NoError(t, err) + + _, err = auth.ForgotPassword(context.Background(), tc.emailAddress) + require.NoError(t, err) + }) + } +} diff --git a/sign_in.go b/sign_in.go index 9fe59a7..cd8189a 100644 --- a/sign_in.go +++ b/sign_in.go @@ -10,7 +10,7 @@ import ( ) // SignIn will attempt to sign a user in, returning the result. -func SignIn(ctx context.Context, emailAddress string, password string) (*types.AuthenticationResultType, error) { +func SignIn(ctx context.Context, emailAddress string, password string) (*cognitoidentityprovider.InitiateAuthOutput, error) { if err := checkPackage(); err != nil { return nil, xerror.Wrap("checkPackage call failed", err) } @@ -21,11 +21,11 @@ func SignIn(ctx context.Context, emailAddress string, password string) (*types.A "USERNAME": emailAddress, "PASSWORD": password, }, - ClientId: aws.String(CognitoUserPoolClientID), + ClientId: aws.String(CognitoClientID), }) if err != nil { return nil, xerror.Wrap("failed to initiate auth", err) } - return output.AuthenticationResult, nil + return output, nil } diff --git a/sign_in_test.go b/sign_in_test.go index 7b30902..783d923 100644 --- a/sign_in_test.go +++ b/sign_in_test.go @@ -25,7 +25,7 @@ func TestSignIn(t *testing.T) { for i, tc := range testCases { t.Run(fmt.Sprintf("TestSignIn_%d", i), func(t *testing.T) { - _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password, tc.password) + _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password) require.NoError(t, err) // Confirm the user without the use of a confirmation code so we can test the sign in. diff --git a/sign_up.go b/sign_up.go index 16f378a..46732d9 100644 --- a/sign_up.go +++ b/sign_up.go @@ -5,19 +5,22 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider" + "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types" "github.com/gofor-little/xerror" ) // SignUp signs a new user up. -func SignUp(ctx context.Context, emailAddress string, password string, passwordConfirmation string) (string, error) { - if password != passwordConfirmation { - return "", xerror.New("password and password confirmation missmatch") - } - +func SignUp(ctx context.Context, emailAddress string, password string) (string, error) { output, err := CognitoClient.SignUp(ctx, &cognitoidentityprovider.SignUpInput{ - ClientId: aws.String(CognitoUserPoolClientID), + ClientId: aws.String(CognitoClientID), Password: aws.String(password), Username: aws.String(emailAddress), + UserAttributes: []types.AttributeType{ + { + Name: aws.String("email"), + Value: aws.String(emailAddress), + }, + }, }) if err != nil { return "", xerror.Wrap("failed to sign up user", err) @@ -29,7 +32,7 @@ func SignUp(ctx context.Context, emailAddress string, password string, passwordC // SignUpConfirm confirms a newly signed up user with the confirmation code they received. func SignUpConfirm(ctx context.Context, emailAddress string, confirmationCode string) error { _, err := CognitoClient.ConfirmSignUp(ctx, &cognitoidentityprovider.ConfirmSignUpInput{ - ClientId: aws.String(CognitoUserPoolClientID), + ClientId: aws.String(CognitoClientID), ConfirmationCode: aws.String(confirmationCode), Username: aws.String(emailAddress), }) diff --git a/sign_up_test.go b/sign_up_test.go index e32f8b6..600af55 100644 --- a/sign_up_test.go +++ b/sign_up_test.go @@ -14,16 +14,15 @@ func TestSignUp(t *testing.T) { defer teardown(t) testCases := []struct { - emailAddress string - password string - passwordConfirmation string + emailAddress string + password string }{ - {"john@example.com", "test-Password1234!!", "test-Password1234!!"}, + {"john@example.com", "test-Password1234!!"}, } for i, tc := range testCases { t.Run(fmt.Sprintf("TestSignUp_%d", i), func(t *testing.T) { - _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password, tc.passwordConfirmation) + _, err := auth.SignUp(context.Background(), tc.emailAddress, tc.password) require.NoError(t, err) }) }