From 008ac4217bc648085b3caba92a4524c31cc0925b Mon Sep 17 00:00:00 2001 From: panteliselef Date: Tue, 5 Mar 2024 19:57:56 +0200 Subject: [PATCH] feat(clerk-js): Experimental CRUD methods for passkeys (#2926) --- .changeset/wet-carpets-double.md | 12 +++++ .../clerk-js/src/core/resources/Passkey.ts | 45 ++++++++++--------- packages/clerk-js/src/core/resources/User.ts | 3 ++ packages/clerk-js/src/utils/passkeys.ts | 1 - packages/types/src/json.ts | 2 +- packages/types/src/passkey.ts | 10 ++++- packages/types/src/user.ts | 6 ++- 7 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 .changeset/wet-carpets-double.md diff --git a/.changeset/wet-carpets-double.md b/.changeset/wet-carpets-double.md new file mode 100644 index 0000000000..0d2e61e08b --- /dev/null +++ b/.changeset/wet-carpets-double.md @@ -0,0 +1,12 @@ +--- +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +Experimental support for reading, updating, and deleting a user's registered passkeys. +- Get the user's passkeys + `clerk.user.__experimental__passkeys` +- Update the name of a passkey + `clerk.user.__experimental__passkeys?.[0].update({name:'work laptop passkey'})` +- Delete a passkey + `clerk.user.__experimental__passkeys?.[0].delete()` diff --git a/packages/clerk-js/src/core/resources/Passkey.ts b/packages/clerk-js/src/core/resources/Passkey.ts index 7bd8ce2ba6..ea2e554b64 100644 --- a/packages/clerk-js/src/core/resources/Passkey.ts +++ b/packages/clerk-js/src/core/resources/Passkey.ts @@ -1,4 +1,11 @@ -import type { PasskeyJSON, PasskeyResource, PasskeyVerificationResource } from '@clerk/types'; +import type { + DeletedObjectJSON, + DeletedObjectResource, + PasskeyJSON, + PasskeyResource, + PasskeyVerificationResource, + UpdatePasskeyParams, +} from '@clerk/types'; import { unixEpochToDate } from '../../utils/date'; import type { PublicKeyCredentialWithAuthenticatorAttestationResponse } from '../../utils/passkeys'; @@ -7,7 +14,7 @@ import { serializePublicKeyCredential, webAuthnCreateCredential, } from '../../utils/passkeys'; -import { BaseResource, ClerkRuntimeError, PasskeyVerification } from './internal'; +import { BaseResource, ClerkRuntimeError, DeletedObject, PasskeyVerification } from './internal'; export class Passkey extends BaseResource implements PasskeyResource { id!: string; @@ -43,13 +50,6 @@ export class Passkey extends BaseResource implements PasskeyResource { }).then(res => new Passkey(res?.response as PasskeyJSON)); } - /** - * TODO-PASSKEYS: Implement this later - * - * GET /v1/me/passkeys - */ - static async get() {} - /** * Developers should not be able to create a new Passkeys from an already instanced object */ @@ -92,25 +92,26 @@ export class Passkey extends BaseResource implements PasskeyResource { } /** - * TODO-PASSKEYS: Implement this later - * * PATCH /v1/me/passkeys/{passkeyIdentificationID} */ - update = (): Promise => this._basePatch(); + update = (params: UpdatePasskeyParams): Promise => + this._basePatch({ + body: params, + }); /** - * TODO-PASSKEYS: Implement this later - * * DELETE /v1/me/passkeys/{passkeyIdentificationID} */ - destroy = (): Promise => this._baseDelete(); - - /** - * TODO-PASSKEYS: Implement this later - * - * GET /v1/me/passkeys/{passkeyIdentificationID} - */ - reload = () => this._baseGet(); + delete = async (): Promise => { + const json = ( + await BaseResource._fetch({ + path: `${this.path()}/${this.id}`, + method: 'DELETE', + }) + )?.response as unknown as DeletedObjectJSON; + + return new DeletedObject(json); + }; protected fromJSON(data: PasskeyJSON | null): this { if (!data) { diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index 176b74872d..d9c8b8db19 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -61,6 +61,7 @@ export class User extends BaseResource implements UserResource { phoneNumbers: PhoneNumberResource[] = []; web3Wallets: Web3WalletResource[] = []; externalAccounts: ExternalAccountResource[] = []; + __experimental_passkeys: PasskeyResource[] = []; samlAccounts: SamlAccountResource[] = []; @@ -337,6 +338,8 @@ export class User extends BaseResource implements UserResource { ea => new ExternalAccount(ea, this.path() + '/external_accounts'), ); + this.__experimental_passkeys = (data.passkeys || []).map(passkey => new Passkey(passkey)); + this.organizationMemberships = (data.organization_memberships || []).map(om => new OrganizationMembership(om)); this.samlAccounts = (data.saml_accounts || []).map(sa => new SamlAccount(sa, this.path() + '/saml_accounts')); diff --git a/packages/clerk-js/src/utils/passkeys.ts b/packages/clerk-js/src/utils/passkeys.ts index 96c800bc18..29f481ae36 100644 --- a/packages/clerk-js/src/utils/passkeys.ts +++ b/packages/clerk-js/src/utils/passkeys.ts @@ -60,7 +60,6 @@ class Base64Converter { } static decode(base64url: string): ArrayBuffer { - // TODO-PASSKEYS: check if this can be replaced with Buffer.from(base64url, 'base64'); const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/'); const binaryString = atob(base64); diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 687c7ac389..65cd815f4a 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -195,7 +195,7 @@ export interface UserJSON extends ClerkResourceJSON { phone_numbers: PhoneNumberJSON[]; web3_wallets: Web3WalletJSON[]; external_accounts: ExternalAccountJSON[]; - + passkeys: PasskeyJSON[]; saml_accounts: SamlAccountJSON[]; organization_memberships: OrganizationMembershipJSON[]; diff --git a/packages/types/src/passkey.ts b/packages/types/src/passkey.ts index dbe691e149..2e2aea8ed3 100644 --- a/packages/types/src/passkey.ts +++ b/packages/types/src/passkey.ts @@ -1,7 +1,12 @@ +import type { DeletedObjectResource } from './deletedObject'; +import type { PasskeyJSON } from './json'; import type { ClerkResource } from './resource'; +import type { SnakeToCamel } from './utils'; import type { PasskeyVerificationResource } from './verification'; -export interface PublicKeyOptions extends PublicKeyCredentialCreationOptions {} +type UpdatePasskeyJSON = Pick; + +export type UpdatePasskeyParams = Partial>; export interface PasskeyResource extends ClerkResource { id: string; @@ -11,4 +16,7 @@ export interface PasskeyResource extends ClerkResource { lastUsedAt: Date | null; updatedAt: Date; createdAt: Date; + + update: (params: UpdatePasskeyParams) => Promise; + delete: () => Promise; } diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index ff4ba0a3e6..8a9ac3c949 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -68,7 +68,11 @@ export interface UserResource extends ClerkResource { phoneNumbers: PhoneNumberResource[]; web3Wallets: Web3WalletResource[]; externalAccounts: ExternalAccountResource[]; - + /** + * @experimental + * This property is experimental, avoid using this in production applications + */ + __experimental_passkeys: PasskeyResource[]; samlAccounts: SamlAccountResource[]; organizationMemberships: OrganizationMembershipResource[];