From 80b1acba80f68cc93465c9dd5e728e8511aadd86 Mon Sep 17 00:00:00 2001 From: Nicolas Lopes Date: Thu, 5 Sep 2024 12:10:38 -0300 Subject: [PATCH] feat: create TOTP --- totp.go | 14 ++++++++++++++ user/api.go | 9 +++++++-- user/client.go | 12 ++++++++++++ user/client_test.go | 21 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 totp.go diff --git a/totp.go b/totp.go new file mode 100644 index 0000000..a2c2cc5 --- /dev/null +++ b/totp.go @@ -0,0 +1,14 @@ +package clerk + +// TOTP describes a TOTP (Time-based One-Time Password) for a user. +type TOTP struct { + APIResource + Object string `json:"object"` + ID string `json:"id"` + Secret *string `json:"secret"` + URI *string `json:"uri" ` + Verified bool `json:"verified"` + BackupCodes []string `json:"backup_codes"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} diff --git a/user/api.go b/user/api.go index e1fc182..3ddacb7 100644 --- a/user/api.go +++ b/user/api.go @@ -23,12 +23,12 @@ func Update(ctx context.Context, id string, params *UpdateParams) (*clerk.User, return getClient().Update(ctx, id, params) } -// UpdateProfileImage sets or replaces the users's profile image. +// UpdateProfileImage sets or replaces the user's profile image. func UpdateProfileImage(ctx context.Context, id string, params *UpdateProfileImageParams) (*clerk.User, error) { return getClient().UpdateProfileImage(ctx, id, params) } -// DeleteProfileImage removes the users's profile image. +// DeleteProfileImage sets or replaces the user's profile image. func DeleteProfileImage(ctx context.Context, id string) (*clerk.User, error) { return getClient().DeleteProfileImage(ctx, id) } @@ -95,6 +95,11 @@ func DeletePasskey(ctx context.Context, userID, identificationID string) (*clerk return getClient().DeletePasskey(ctx, userID, identificationID) } +// CreateTOTP creates a TOTP (Time-based One-Time Password) for the user. +func CreateTOTP(ctx context.Context, userID string) (*clerk.TOTP, error) { + return getClient().CreateTOTP(ctx, userID) +} + func getClient() *Client { return &Client{ Backend: clerk.GetBackend(), diff --git a/user/client.go b/user/client.go index e09a399..f774b79 100644 --- a/user/client.go +++ b/user/client.go @@ -440,3 +440,15 @@ func (c *Client) DeletePasskey(ctx context.Context, userID, identificationID str err = c.Backend.Call(ctx, req, resource) return resource, err } + +// CreateTOTP creates a TOTP (Time-based One-Time Password) for the user. +func (c *Client) CreateTOTP(ctx context.Context, userID string) (*clerk.TOTP, error) { + path, err := clerk.JoinPath(path, userID, "/totp") + if err != nil { + return nil, err + } + req := clerk.NewAPIRequest(http.MethodPost, path) + resource := &clerk.TOTP{} + err = c.Backend.Call(ctx, req, resource) + return resource, err +} diff --git a/user/client_test.go b/user/client_test.go index dbab216..e5c1fbf 100644 --- a/user/client_test.go +++ b/user/client_test.go @@ -441,3 +441,24 @@ func TestUserClientDeletePasskey(t *testing.T) { require.NoError(t, err) require.Equal(t, passkeyIdentificationID, passkey.ID) } + +func TestUserClientCreateTOTP(t *testing.T) { + t.Parallel() + userID := "user_123" + config := &clerk.ClientConfig{} + config.HTTPClient = &http.Client{ + Transport: &clerktest.RoundTripper{ + T: t, + Method: http.MethodPost, + Out: json.RawMessage(`{"backup_codes":null,"created_at":1725548779338,"id":"totp_id","object":"totp","secret":"secret","updated_at":1725548779338,"uri":"otpauth://totp/","verified":false}`), + Path: fmt.Sprintf("/v1/users/%s/totp", userID), + }, + } + client := NewClient(config) + totp, err := client.CreateTOTP(context.Background(), userID) + require.NoError(t, err) + require.NotNil(t, totp.ID) + require.NotNil(t, totp.Secret) + require.NotNil(t, totp.URI) + require.Equal(t, totp.Object, "totp") +}