From 29a27056495f52b4a93034555166cfc7f781a199 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 12 Oct 2026 17:56:46 +0100 Subject: [PATCH] feat: add passkeys for version 29.3.0 --- CHANGELOG.md | 8 + docs/examples/account/create-passkey-token.md | 12 + docs/examples/account/create-passkey.md | 15 + docs/examples/account/delete-passkey.md | 14 + docs/examples/account/get-passkey.md | 14 + docs/examples/account/list-passkeys.md | 15 + docs/examples/account/update-passkey-token.md | 15 + .../account/update-passkey-verification.md | 15 + docs/examples/account/update-passkey.md | 15 + .../examples/project/update-passkey-policy.md | 14 + docs/examples/users/delete-passkey.md | 15 + docs/examples/users/get-passkey.md | 15 + docs/examples/users/list-passkeys.md | 16 + docs/examples/users/update-passkey.md | 16 + package-lock.json | 4 +- package.json | 2 +- src/client.ts | 4 +- src/models.ts | 96 ++++ src/services/account.ts | 495 ++++++++++++++++++ src/services/project.ts | 54 ++ src/services/users.ts | 301 +++++++++++ test/services/account.test.js | 123 +++++ test/services/project.test.js | 37 ++ test/services/users.test.js | 59 +++ 24 files changed, 1369 insertions(+), 5 deletions(-) create mode 100644 docs/examples/account/create-passkey-token.md create mode 100644 docs/examples/account/create-passkey.md create mode 100644 docs/examples/account/delete-passkey.md create mode 100644 docs/examples/account/get-passkey.md create mode 100644 docs/examples/account/list-passkeys.md create mode 100644 docs/examples/account/update-passkey-token.md create mode 100644 docs/examples/account/update-passkey-verification.md create mode 100644 docs/examples/account/update-passkey.md create mode 100644 docs/examples/project/update-passkey-policy.md create mode 100644 docs/examples/users/delete-passkey.md create mode 100644 docs/examples/users/get-passkey.md create mode 100644 docs/examples/users/list-passkeys.md create mode 100644 docs/examples/users/update-passkey.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 58435a3..4dd7b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 29.3.0 + +* Added: `account` passkey methods `listPasskeys`, `createPasskey`, `getPasskey`, `updatePasskey`, `deletePasskey` and `updatePasskeyVerification` +* Added: `account.createPasskeyToken` and `account.updatePasskeyToken` for passkey sign-in +* Added: `users.listPasskeys`, `users.getPasskey`, `users.updatePasskey` and `users.deletePasskey` +* Added: `project.updatePasskeyPolicy` to configure passkey sign-in +* Added: `Passkey`, `PasskeyList`, `PasskeyChallenge` and `TokenSecret` models + ## 29.2.0 * Added: optional `interval` parameter on `functions.create` and `functions.update` to run a function every N minutes diff --git a/docs/examples/account/create-passkey-token.md b/docs/examples/account/create-passkey-token.md new file mode 100644 index 0000000..cf4a5c7 --- /dev/null +++ b/docs/examples/account/create-passkey-token.md @@ -0,0 +1,12 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.createPasskeyToken(); +``` diff --git a/docs/examples/account/create-passkey.md b/docs/examples/account/create-passkey.md new file mode 100644 index 0000000..50bdf74 --- /dev/null +++ b/docs/examples/account/create-passkey.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.createPasskey({ + passkeyId: '', // optional + name: '', // optional +}); +``` diff --git a/docs/examples/account/delete-passkey.md b/docs/examples/account/delete-passkey.md new file mode 100644 index 0000000..d19b717 --- /dev/null +++ b/docs/examples/account/delete-passkey.md @@ -0,0 +1,14 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.deletePasskey({ + passkeyId: '', +}); +``` diff --git a/docs/examples/account/get-passkey.md b/docs/examples/account/get-passkey.md new file mode 100644 index 0000000..3a125d0 --- /dev/null +++ b/docs/examples/account/get-passkey.md @@ -0,0 +1,14 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.getPasskey({ + passkeyId: '', +}); +``` diff --git a/docs/examples/account/list-passkeys.md b/docs/examples/account/list-passkeys.md new file mode 100644 index 0000000..403da40 --- /dev/null +++ b/docs/examples/account/list-passkeys.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.listPasskeys({ + queries: [], // optional + total: false, // optional +}); +``` diff --git a/docs/examples/account/update-passkey-token.md b/docs/examples/account/update-passkey-token.md new file mode 100644 index 0000000..1bdc685 --- /dev/null +++ b/docs/examples/account/update-passkey-token.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.updatePasskeyToken({ + challengeId: '', + credential: {}, +}); +``` diff --git a/docs/examples/account/update-passkey-verification.md b/docs/examples/account/update-passkey-verification.md new file mode 100644 index 0000000..d3294a1 --- /dev/null +++ b/docs/examples/account/update-passkey-verification.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.updatePasskeyVerification({ + passkeyId: '', + credential: {}, +}); +``` diff --git a/docs/examples/account/update-passkey.md b/docs/examples/account/update-passkey.md new file mode 100644 index 0000000..f296b3e --- /dev/null +++ b/docs/examples/account/update-passkey.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setSession(''); // The user session to authenticate with + +const account = new sdk.Account(client); + +const result = await account.updatePasskey({ + passkeyId: '', + name: '', +}); +``` diff --git a/docs/examples/project/update-passkey-policy.md b/docs/examples/project/update-passkey-policy.md new file mode 100644 index 0000000..c289240 --- /dev/null +++ b/docs/examples/project/update-passkey-policy.md @@ -0,0 +1,14 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const project = new sdk.Project(client); + +const result = await project.updatePasskeyPolicy({ + rpId: '', +}); +``` diff --git a/docs/examples/users/delete-passkey.md b/docs/examples/users/delete-passkey.md new file mode 100644 index 0000000..0a8b24b --- /dev/null +++ b/docs/examples/users/delete-passkey.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const users = new sdk.Users(client); + +const result = await users.deletePasskey({ + userId: '', + passkeyId: '', +}); +``` diff --git a/docs/examples/users/get-passkey.md b/docs/examples/users/get-passkey.md new file mode 100644 index 0000000..f4f8ac7 --- /dev/null +++ b/docs/examples/users/get-passkey.md @@ -0,0 +1,15 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const users = new sdk.Users(client); + +const result = await users.getPasskey({ + userId: '', + passkeyId: '', +}); +``` diff --git a/docs/examples/users/list-passkeys.md b/docs/examples/users/list-passkeys.md new file mode 100644 index 0000000..cd08eeb --- /dev/null +++ b/docs/examples/users/list-passkeys.md @@ -0,0 +1,16 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const users = new sdk.Users(client); + +const result = await users.listPasskeys({ + userId: '', + queries: [], // optional + total: false, // optional +}); +``` diff --git a/docs/examples/users/update-passkey.md b/docs/examples/users/update-passkey.md new file mode 100644 index 0000000..b6572ba --- /dev/null +++ b/docs/examples/users/update-passkey.md @@ -0,0 +1,16 @@ +```javascript +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const users = new sdk.Users(client); + +const result = await users.updatePasskey({ + userId: '', + passkeyId: '', + name: '', +}); +``` diff --git a/package-lock.json b/package-lock.json index 80586bf..f13b7da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "node-appwrite", - "version": "29.2.0", + "version": "29.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "node-appwrite", - "version": "29.2.0", + "version": "29.3.0", "license": "BSD-3-Clause", "dependencies": { "json-bigint": "1.0.0", diff --git a/package.json b/package.json index 10dff5d..71de758 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "node-appwrite", "homepage": "https://appwrite.io/support", "description": "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API", - "version": "29.2.0", + "version": "29.3.0", "license": "BSD-3-Clause", "main": "dist/index.js", "type": "commonjs", diff --git a/src/client.ts b/src/client.ts index ea05002..d706e4b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -80,7 +80,7 @@ class AppwriteException extends Error { } function getUserAgent() { - let ua = 'AppwriteNodeJSSDK/29.2.0'; + let ua = 'AppwriteNodeJSSDK/29.3.0'; // `process` is a global in Node.js, but not fully available in all runtimes. const platform: string[] = []; @@ -142,7 +142,7 @@ class Client { 'x-sdk-name': 'Node.js', 'x-sdk-platform': 'server', 'x-sdk-language': 'nodejs', - 'x-sdk-version': '29.2.0', + 'x-sdk-version': '29.3.0', 'user-agent': getUserAgent(), 'X-Appwrite-Response-Format': '2.0.0', }; diff --git a/src/models.ts b/src/models.ts index eb0dc9d..3839058 100644 --- a/src/models.ts +++ b/src/models.ts @@ -6760,6 +6760,102 @@ export namespace Models { code: string; }; + /** + * Token Secret + */ + export type TokenSecret = { + /** + * Token ID. + */ + $id: string; + /** + * Token creation date in ISO 8601 format. + */ + $createdAt: string; + /** + * User ID. + */ + userId: string; + /** + * Token secret key. Exchange it for a session with the create session endpoint. + */ + secret: string; + /** + * Token expiration date in ISO 8601 format. + */ + expire: string; + /** + * Security phrase of a token. Empty if security phrase was not requested when creating a token. It includes randomly generated phrase which is also sent in the external resource such as email. + */ + phrase: string; + }; + + /** + * Passkey + */ + export type Passkey = { + /** + * Passkey ID. + */ + $id: string; + /** + * Passkey creation date in ISO 8601 format. + */ + $createdAt: string; + /** + * Passkey update date in ISO 8601 format. + */ + $updatedAt: string; + /** + * Passkey name. + */ + name: string; + /** + * Most recent sign-in with this passkey in ISO 8601 format. Empty until the passkey is used to sign in. + */ + accessedAt: string; + }; + + /** + * Passkey Challenge + */ + export type PasskeyChallenge = { + /** + * Challenge ID. When signing in, pass it back with the credential to receive a token. + */ + $id: string; + /** + * Challenge creation date in ISO 8601 format. + */ + $createdAt: string; + /** + * ID of the passkey being registered. Empty for sign-in challenges. + */ + passkeyId: string; + /** + * Challenge expiration date in ISO 8601 format. + */ + expire: string; + /** + * WebAuthn options in JSON form. Pass them to `PublicKeyCredential.parseCreationOptionsFromJSON()` when registering, or `PublicKeyCredential.parseRequestOptionsFromJSON()` when signing in. + */ + publicKey: object; + }; + + /** + * Passkeys List + */ + export type PasskeyList = { + /** + * Total number of passkeys that matched your query. + */ + total: number; + /** + * List of passkeys. + */ + passkeys: Passkey[]; + }; + /** * MFA Recovery Codes */ diff --git a/src/services/account.ts b/src/services/account.ts index bd68990..73519d1 100644 --- a/src/services/account.ts +++ b/src/services/account.ts @@ -1750,6 +1750,403 @@ export class Account { return this.client.call('patch', uri, apiHeaders, apiPayload); } + /** + * Get the list of verified passkeys registered by the currently logged in user. + * + * @param {string[]} params.queries - Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter and order on the following attributes: $id, $createdAt, $updatedAt, name, accessedAt + * @param {boolean} params.total - When set to false, the total count returned will be 0 and will not be calculated. + * @throws {AppwriteException} + * @returns {Promise} + */ + listPasskeys(params?: { + queries?: string[]; + total?: boolean; + }): Promise; + /** + * Get the list of verified passkeys registered by the currently logged in user. + * + * @param {string[]} queries - Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter and order on the following attributes: $id, $createdAt, $updatedAt, name, accessedAt + * @param {boolean} total - When set to false, the total count returned will be 0 and will not be calculated. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + listPasskeys( + queries?: string[], + total?: boolean, + ): Promise; + listPasskeys( + paramsOrFirst?: { queries?: string[]; total?: boolean } | string[], + ...rest: [boolean?] + ): Promise { + let params: { queries?: string[]; total?: boolean }; + + if ( + (typeof paramsOrFirst === 'undefined' && rest.length === 0) || + (paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst)) + ) { + params = (paramsOrFirst || {}) as { + queries?: string[]; + total?: boolean; + }; + } else { + params = { + queries: paramsOrFirst as string[], + total: rest[0] as boolean, + }; + } + + const queries = params.queries; + const total = params.total; + const apiPath = '/account/passkeys'; + const apiPayload: Payload = {}; + if (typeof queries !== 'undefined') { + apiPayload['queries'] = queries; + } + if (typeof total !== 'undefined') { + apiPayload['total'] = total; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + accept: 'application/json', + }; + + return this.client.call('get', uri, apiHeaders, apiPayload); + } + + /** + * Start registering a passkey for the currently logged in user. The session must have signed in or completed an MFA challenge within the last 10 minutes. Pass the returned `publicKey` options to `navigator.credentials.create()`, then complete the registration with [Update passkey verification](/docs/references/cloud/client-web/account#updatePasskeyVerification). The passkey stays pending until verified, and the challenge expires after 5 minutes. + * + * @param {string} params.passkeyId - Passkey ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. + * @param {string} params.name - Passkey name, shown when listing passkeys. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + */ + createPasskey(params?: { + passkeyId?: string; + name?: string; + }): Promise; + /** + * Start registering a passkey for the currently logged in user. The session must have signed in or completed an MFA challenge within the last 10 minutes. Pass the returned `publicKey` options to `navigator.credentials.create()`, then complete the registration with [Update passkey verification](/docs/references/cloud/client-web/account#updatePasskeyVerification). The passkey stays pending until verified, and the challenge expires after 5 minutes. + * + * @param {string} passkeyId - Passkey ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. + * @param {string} name - Passkey name, shown when listing passkeys. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + createPasskey( + passkeyId?: string, + name?: string, + ): Promise; + createPasskey( + paramsOrFirst?: { passkeyId?: string; name?: string } | string, + ...rest: [string?] + ): Promise { + let params: { passkeyId?: string; name?: string }; + + if ( + (typeof paramsOrFirst === 'undefined' && rest.length === 0) || + (paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst)) + ) { + params = (paramsOrFirst || {}) as { + passkeyId?: string; + name?: string; + }; + } else { + params = { + passkeyId: paramsOrFirst as string, + name: rest[0] as string, + }; + } + + const passkeyId = params.passkeyId; + const name = params.name; + const apiPath = '/account/passkeys'; + const apiPayload: Payload = {}; + if (typeof passkeyId !== 'undefined') { + apiPayload['passkeyId'] = passkeyId; + } + if (typeof name !== 'undefined') { + apiPayload['name'] = name; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('post', uri, apiHeaders, apiPayload); + } + + /** + * Get a verified passkey of the currently logged in user by its unique ID. + * + * @param {string} params.passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise} + */ + getPasskey(params: { passkeyId: string }): Promise; + /** + * Get a verified passkey of the currently logged in user by its unique ID. + * + * @param {string} passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + getPasskey(passkeyId: string): Promise; + getPasskey( + paramsOrFirst: { passkeyId: string } | string, + ): Promise { + let params: { passkeyId: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { passkeyId: string }; + } else { + params = { + passkeyId: paramsOrFirst as string, + }; + } + + const passkeyId = params.passkeyId; + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + const apiPath = '/account/passkeys/{passkeyId}'.replace( + '{passkeyId}', + encodeURIComponent(String(passkeyId)), + ); + const apiPayload: Payload = {}; + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + accept: 'application/json', + }; + + return this.client.call('get', uri, apiHeaders, apiPayload); + } + + /** + * Rename a passkey of the currently logged in user. + * + * @param {string} params.passkeyId - Passkey ID. + * @param {string} params.name - Passkey name. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + */ + updatePasskey(params: { + passkeyId: string; + name: string; + }): Promise; + /** + * Rename a passkey of the currently logged in user. + * + * @param {string} passkeyId - Passkey ID. + * @param {string} name - Passkey name. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updatePasskey(passkeyId: string, name: string): Promise; + updatePasskey( + paramsOrFirst: { passkeyId: string; name: string } | string, + ...rest: [string?] + ): Promise { + let params: { passkeyId: string; name: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + passkeyId: string; + name: string; + }; + } else { + params = { + passkeyId: paramsOrFirst as string, + name: rest[0] as string, + }; + } + + const passkeyId = params.passkeyId; + const name = params.name; + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + if (typeof name === 'undefined') { + throw new AppwriteException('Missing required parameter: "name"'); + } + const apiPath = '/account/passkeys/{passkeyId}'.replace( + '{passkeyId}', + encodeURIComponent(String(passkeyId)), + ); + const apiPayload: Payload = {}; + if (typeof name !== 'undefined') { + apiPayload['name'] = name; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('patch', uri, apiHeaders, apiPayload); + } + + /** + * Delete a passkey from the currently logged in user. The passkey can no longer be used to sign in, although it may remain stored on the user's device. The session must have signed in or completed an MFA challenge within the last 10 minutes. + * + * @param {string} params.passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise<{}>} + */ + deletePasskey(params: { passkeyId: string }): Promise<{}>; + /** + * Delete a passkey from the currently logged in user. The passkey can no longer be used to sign in, although it may remain stored on the user's device. The session must have signed in or completed an MFA challenge within the last 10 minutes. + * + * @param {string} passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise<{}>} + * @deprecated Use the object parameter style method for a better developer experience. + */ + deletePasskey(passkeyId: string): Promise<{}>; + deletePasskey(paramsOrFirst: { passkeyId: string } | string): Promise<{}> { + let params: { passkeyId: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { passkeyId: string }; + } else { + params = { + passkeyId: paramsOrFirst as string, + }; + } + + const passkeyId = params.passkeyId; + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + const apiPath = '/account/passkeys/{passkeyId}'.replace( + '{passkeyId}', + encodeURIComponent(String(passkeyId)), + ); + const apiPayload: Payload = {}; + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('delete', uri, apiHeaders, apiPayload); + } + + /** + * Complete a passkey registration started with [Create passkey](/docs/references/cloud/client-web/account#createPasskey). Pass the JSON form of the credential returned by `navigator.credentials.create()`, for example `credential.toJSON()`. Each registration can be verified once; a failed attempt requires starting it again. + * + * @param {string} params.passkeyId - Passkey ID. + * @param {object} params.credential - Registration credential returned by the authenticator, in the JSON form produced by `PublicKeyCredential.toJSON()`. + * @throws {AppwriteException} + * @returns {Promise} + */ + updatePasskeyVerification(params: { + passkeyId: string; + credential: object; + }): Promise; + /** + * Complete a passkey registration started with [Create passkey](/docs/references/cloud/client-web/account#createPasskey). Pass the JSON form of the credential returned by `navigator.credentials.create()`, for example `credential.toJSON()`. Each registration can be verified once; a failed attempt requires starting it again. + * + * @param {string} passkeyId - Passkey ID. + * @param {object} credential - Registration credential returned by the authenticator, in the JSON form produced by `PublicKeyCredential.toJSON()`. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updatePasskeyVerification( + passkeyId: string, + credential: object, + ): Promise; + updatePasskeyVerification( + paramsOrFirst: { passkeyId: string; credential: object } | string, + ...rest: [object?] + ): Promise { + let params: { passkeyId: string; credential: object }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + passkeyId: string; + credential: object; + }; + } else { + params = { + passkeyId: paramsOrFirst as string, + credential: rest[0] as object, + }; + } + + const passkeyId = params.passkeyId; + const credential = params.credential; + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + if (typeof credential === 'undefined') { + throw new AppwriteException( + 'Missing required parameter: "credential"', + ); + } + const apiPath = '/account/passkeys/{passkeyId}/verification'.replace( + '{passkeyId}', + encodeURIComponent(String(passkeyId)), + ); + const apiPayload: Payload = {}; + if (typeof credential !== 'undefined') { + apiPayload['credential'] = credential; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('put', uri, apiHeaders, apiPayload); + } + /** * Update currently logged in user password. For validation, user is required to pass in the new password, and the old password. For users created with OAuth, Team Invites and Magic URL, oldPassword is optional. * @@ -3023,6 +3420,104 @@ export class Account { return this.client.redirect('get', uri, apiHeaders, apiPayload); } + /** + * Start signing in with a passkey. No email or user ID is needed: pass the returned `publicKey` options to `navigator.credentials.get()` and the user picks one of their passkeys. Then call [Update passkey token](/docs/references/cloud/client-web/account#updatePasskeyToken) with the credential to receive a token, and exchange it with [Create session](/docs/references/cloud/client-web/account#createSession). The challenge expires after 5 minutes. + * + * @throws {AppwriteException} + * @returns {Promise} + */ + createPasskeyToken(): Promise { + const apiPath = '/account/tokens/passkey'; + const apiPayload: Payload = {}; + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('post', uri, apiHeaders, apiPayload); + } + + /** + * Complete a passkey sign-in started with [Create passkey token](/docs/references/cloud/client-web/account#createPasskeyToken). Pass the challenge ID and the JSON form of the credential returned by `navigator.credentials.get()`, for example `credential.toJSON()`. The returned token includes its secret and expires after 1 minute: exchange it for a session with [Create session](/docs/references/cloud/client-web/account#createSession). Passkeys require user verification, so the session also satisfies MFA. Each challenge can only be used once. + * + * @param {string} params.challengeId - Challenge ID returned by createPasskeyToken. + * @param {object} params.credential - Authentication credential returned by the authenticator, in the JSON form produced by `PublicKeyCredential.toJSON()`. + * @throws {AppwriteException} + * @returns {Promise} + */ + updatePasskeyToken(params: { + challengeId: string; + credential: object; + }): Promise; + /** + * Complete a passkey sign-in started with [Create passkey token](/docs/references/cloud/client-web/account#createPasskeyToken). Pass the challenge ID and the JSON form of the credential returned by `navigator.credentials.get()`, for example `credential.toJSON()`. The returned token includes its secret and expires after 1 minute: exchange it for a session with [Create session](/docs/references/cloud/client-web/account#createSession). Passkeys require user verification, so the session also satisfies MFA. Each challenge can only be used once. + * + * @param {string} challengeId - Challenge ID returned by createPasskeyToken. + * @param {object} credential - Authentication credential returned by the authenticator, in the JSON form produced by `PublicKeyCredential.toJSON()`. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updatePasskeyToken( + challengeId: string, + credential: object, + ): Promise; + updatePasskeyToken( + paramsOrFirst: { challengeId: string; credential: object } | string, + ...rest: [object?] + ): Promise { + let params: { challengeId: string; credential: object }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + challengeId: string; + credential: object; + }; + } else { + params = { + challengeId: paramsOrFirst as string, + credential: rest[0] as object, + }; + } + + const challengeId = params.challengeId; + const credential = params.credential; + if (typeof challengeId === 'undefined') { + throw new AppwriteException( + 'Missing required parameter: "challengeId"', + ); + } + if (typeof credential === 'undefined') { + throw new AppwriteException( + 'Missing required parameter: "credential"', + ); + } + const apiPath = '/account/tokens/passkey'; + const apiPayload: Payload = {}; + if (typeof challengeId !== 'undefined') { + apiPayload['challengeId'] = challengeId; + } + if (typeof credential !== 'undefined') { + apiPayload['credential'] = credential; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('put', uri, apiHeaders, apiPayload); + } + /** * Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST /v1/account/sessions/token](https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes. * diff --git a/src/services/project.ts b/src/services/project.ts index cc63f31..3f898b2 100644 --- a/src/services/project.ts +++ b/src/services/project.ts @@ -7245,6 +7245,60 @@ export class Project { return this.client.call('patch', uri, apiHeaders, apiPayload); } + /** + * Set the relying party ID passkeys are bound to: the domain of your application. Passkeys work on the project's web platforms on that domain or its subdomains, and on the domain itself for Apple apps. A localhost web platform enables passkeys for local development without a relying party ID. The relying party ID cannot change while users have passkeys, and any change invalidates ceremonies in progress. + * + * @param {string} params.rpId - Relying party ID: the domain of your application, such as `example.com`. Use `localhost` for local development, or an empty string to unset it. + * @throws {AppwriteException} + * @returns {Promise} + */ + updatePasskeyPolicy(params: { rpId: string }): Promise; + /** + * Set the relying party ID passkeys are bound to: the domain of your application. Passkeys work on the project's web platforms on that domain or its subdomains, and on the domain itself for Apple apps. A localhost web platform enables passkeys for local development without a relying party ID. The relying party ID cannot change while users have passkeys, and any change invalidates ceremonies in progress. + * + * @param {string} rpId - Relying party ID: the domain of your application, such as `example.com`. Use `localhost` for local development, or an empty string to unset it. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updatePasskeyPolicy(rpId: string): Promise; + updatePasskeyPolicy( + paramsOrFirst: { rpId: string } | string, + ): Promise { + let params: { rpId: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { rpId: string }; + } else { + params = { + rpId: paramsOrFirst as string, + }; + } + + const rpId = params.rpId; + if (typeof rpId === 'undefined') { + throw new AppwriteException('Missing required parameter: "rpId"'); + } + const apiPath = '/project/policies/passkey'; + const apiPayload: Payload = {}; + if (typeof rpId !== 'undefined') { + apiPayload['rpId'] = rpId; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('patch', uri, apiHeaders, apiPayload); + } + /** * Updating this policy allows you to control if new passwords are checked against most common passwords dictionary. When enabled, and user changes their password, password must not be contained in the dictionary. * diff --git a/src/services/users.ts b/src/services/users.ts index be306a8..bcc7aa6 100644 --- a/src/services/users.ts +++ b/src/services/users.ts @@ -2785,6 +2785,307 @@ export class Users { return this.client.call('patch', uri, apiHeaders, apiPayload); } + /** + * Get the list of verified passkeys registered by a user. + * + * @param {string} params.userId - User ID. + * @param {string[]} params.queries - Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter and order on the following attributes: $id, $createdAt, $updatedAt, name, accessedAt + * @param {boolean} params.total - When set to false, the total count returned will be 0 and will not be calculated. + * @throws {AppwriteException} + * @returns {Promise} + */ + listPasskeys(params: { + userId: string; + queries?: string[]; + total?: boolean; + }): Promise; + /** + * Get the list of verified passkeys registered by a user. + * + * @param {string} userId - User ID. + * @param {string[]} queries - Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter and order on the following attributes: $id, $createdAt, $updatedAt, name, accessedAt + * @param {boolean} total - When set to false, the total count returned will be 0 and will not be calculated. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + listPasskeys( + userId: string, + queries?: string[], + total?: boolean, + ): Promise; + listPasskeys( + paramsOrFirst: + { userId: string; queries?: string[]; total?: boolean } | string, + ...rest: [string[]?, boolean?] + ): Promise { + let params: { userId: string; queries?: string[]; total?: boolean }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + queries?: string[]; + total?: boolean; + }; + } else { + params = { + userId: paramsOrFirst as string, + queries: rest[0] as string[], + total: rest[1] as boolean, + }; + } + + const userId = params.userId; + const queries = params.queries; + const total = params.total; + if (typeof userId === 'undefined' || userId === '') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + const apiPath = '/users/{userId}/passkeys'.replace( + '{userId}', + encodeURIComponent(String(userId)), + ); + const apiPayload: Payload = {}; + if (typeof queries !== 'undefined') { + apiPayload['queries'] = queries; + } + if (typeof total !== 'undefined') { + apiPayload['total'] = total; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + accept: 'application/json', + }; + + return this.client.call('get', uri, apiHeaders, apiPayload); + } + + /** + * Get a verified passkey of a user by its unique ID. + * + * @param {string} params.userId - User ID. + * @param {string} params.passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise} + */ + getPasskey(params: { + userId: string; + passkeyId: string; + }): Promise; + /** + * Get a verified passkey of a user by its unique ID. + * + * @param {string} userId - User ID. + * @param {string} passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + getPasskey(userId: string, passkeyId: string): Promise; + getPasskey( + paramsOrFirst: { userId: string; passkeyId: string } | string, + ...rest: [string?] + ): Promise { + let params: { userId: string; passkeyId: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + passkeyId: string; + }; + } else { + params = { + userId: paramsOrFirst as string, + passkeyId: rest[0] as string, + }; + } + + const userId = params.userId; + const passkeyId = params.passkeyId; + if (typeof userId === 'undefined' || userId === '') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + const apiPath = '/users/{userId}/passkeys/{passkeyId}' + .replace('{userId}', encodeURIComponent(String(userId))) + .replace('{passkeyId}', encodeURIComponent(String(passkeyId))); + const apiPayload: Payload = {}; + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + accept: 'application/json', + }; + + return this.client.call('get', uri, apiHeaders, apiPayload); + } + + /** + * Rename a passkey of a user. + * + * @param {string} params.userId - User ID. + * @param {string} params.passkeyId - Passkey ID. + * @param {string} params.name - Passkey name. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + */ + updatePasskey(params: { + userId: string; + passkeyId: string; + name: string; + }): Promise; + /** + * Rename a passkey of a user. + * + * @param {string} userId - User ID. + * @param {string} passkeyId - Passkey ID. + * @param {string} name - Passkey name. Max length: 128 chars. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updatePasskey( + userId: string, + passkeyId: string, + name: string, + ): Promise; + updatePasskey( + paramsOrFirst: + { userId: string; passkeyId: string; name: string } | string, + ...rest: [string?, string?] + ): Promise { + let params: { userId: string; passkeyId: string; name: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + passkeyId: string; + name: string; + }; + } else { + params = { + userId: paramsOrFirst as string, + passkeyId: rest[0] as string, + name: rest[1] as string, + }; + } + + const userId = params.userId; + const passkeyId = params.passkeyId; + const name = params.name; + if (typeof userId === 'undefined' || userId === '') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + if (typeof name === 'undefined') { + throw new AppwriteException('Missing required parameter: "name"'); + } + const apiPath = '/users/{userId}/passkeys/{passkeyId}' + .replace('{userId}', encodeURIComponent(String(userId))) + .replace('{passkeyId}', encodeURIComponent(String(passkeyId))); + const apiPayload: Payload = {}; + if (typeof name !== 'undefined') { + apiPayload['name'] = name; + } + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('patch', uri, apiHeaders, apiPayload); + } + + /** + * Delete a passkey from a user, for example when a device is lost. The passkey can no longer be used to sign in. + * + * @param {string} params.userId - User ID. + * @param {string} params.passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise<{}>} + */ + deletePasskey(params: { userId: string; passkeyId: string }): Promise<{}>; + /** + * Delete a passkey from a user, for example when a device is lost. The passkey can no longer be used to sign in. + * + * @param {string} userId - User ID. + * @param {string} passkeyId - Passkey ID. + * @throws {AppwriteException} + * @returns {Promise<{}>} + * @deprecated Use the object parameter style method for a better developer experience. + */ + deletePasskey(userId: string, passkeyId: string): Promise<{}>; + deletePasskey( + paramsOrFirst: { userId: string; passkeyId: string } | string, + ...rest: [string?] + ): Promise<{}> { + let params: { userId: string; passkeyId: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + passkeyId: string; + }; + } else { + params = { + userId: paramsOrFirst as string, + passkeyId: rest[0] as string, + }; + } + + const userId = params.userId; + const passkeyId = params.passkeyId; + if (typeof userId === 'undefined' || userId === '') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + if (typeof passkeyId === 'undefined' || passkeyId === '') { + throw new AppwriteException( + 'Missing required parameter: "passkeyId"', + ); + } + const apiPath = '/users/{userId}/passkeys/{passkeyId}' + .replace('{userId}', encodeURIComponent(String(userId))) + .replace('{passkeyId}', encodeURIComponent(String(passkeyId))); + const apiPayload: Payload = {}; + const uri = new URL(this.client.config.endpoint + apiPath); + + const apiHeaders: { [header: string]: string } = { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }; + + return this.client.call('delete', uri, apiHeaders, apiPayload); + } + /** * Update the user password by its unique ID. * diff --git a/test/services/account.test.js b/test/services/account.test.js index 3f7f45e..072fc13 100644 --- a/test/services/account.test.js +++ b/test/services/account.test.js @@ -606,6 +606,96 @@ describe('Account', () => { expect(response).toEqual(data); }); + test('test method listPasskeys()', async () => { + const data = { + total: 5, + passkeys: [], + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.listPasskeys(); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method createPasskey()', async () => { + const data = { + '\\$id': 'bb8ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + passkeyId: '5e5ea5c16897e', + expire: '2020-10-15T06:38:00.000+00:00', + publicKey: {}, + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.createPasskey(); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method getPasskey()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'My laptop', + accessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.getPasskey(''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method updatePasskey()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'My laptop', + accessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.updatePasskey('', ''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method deletePasskey()', async () => { + const data = { message: '' }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.deletePasskey(''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method updatePasskeyVerification()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'My laptop', + accessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.updatePasskeyVerification( + '', + {}, + ); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); test('test method updatePassword()', async () => { const data = { '\\$id': '5e5ea5c16897e', @@ -1137,6 +1227,39 @@ describe('Account', () => { expect(response).toEqual(data); }); + test('test method createPasskeyToken()', async () => { + const data = { + '\\$id': 'bb8ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + passkeyId: '5e5ea5c16897e', + expire: '2020-10-15T06:38:00.000+00:00', + publicKey: {}, + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.createPasskeyToken(); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method updatePasskeyToken()', async () => { + const data = { + '\\$id': 'bb8ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + userId: '5e5ea5c168bb8', + secret: '', + expire: '2020-10-15T06:38:00.000+00:00', + phrase: 'Golden Fox', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await account.updatePasskeyToken('', {}); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); test('test method createPhoneToken()', async () => { const data = { '\\$id': 'bb8ea5c16897e', diff --git a/test/services/project.test.js b/test/services/project.test.js index 026c953..6937937 100644 --- a/test/services/project.test.js +++ b/test/services/project.test.js @@ -1579,6 +1579,43 @@ describe('Project', () => { expect(response).toEqual(data); }); + test('test method updatePasskeyPolicy()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'New Project', + teamId: '1592981250', + region: 'fra', + smtpEnabled: true, + smtpSenderName: 'John Appwrite', + smtpSenderEmail: 'john@appwrite.io', + smtpReplyToName: 'Support Team', + smtpReplyToEmail: 'support@appwrite.io', + smtpHost: 'mail.appwrite.io', + smtpPort: 25, + smtpUsername: 'emailuser', + smtpPassword: 'smtp-password', + smtpSecure: 'tls', + pingCount: 1, + pingedAt: '2020-10-15T06:38:00.000+00:00', + labels: [], + status: 'active', + onboarding: {}, + authMethods: [], + services: [], + protocols: [], + blocks: [], + consoleAccessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await project.updatePasskeyPolicy(''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); test('test method updatePasswordDictionaryPolicy()', async () => { const data = { '\\$id': '5e5ea5c16897e', diff --git a/test/services/users.test.js b/test/services/users.test.js index 0e61c5f..ef14c35 100644 --- a/test/services/users.test.js +++ b/test/services/users.test.js @@ -688,6 +688,65 @@ describe('Users', () => { expect(response).toEqual(data); }); + test('test method listPasskeys()', async () => { + const data = { + total: 5, + passkeys: [], + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await users.listPasskeys(''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method getPasskey()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'My laptop', + accessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await users.getPasskey('', ''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method updatePasskey()', async () => { + const data = { + '\\$id': '5e5ea5c16897e', + '\\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\\$updatedAt': '2020-10-15T06:38:00.000+00:00', + name: 'My laptop', + accessedAt: '2020-10-15T06:38:00.000+00:00', + }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await users.updatePasskey( + '', + '', + '', + ); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); + test('test method deletePasskey()', async () => { + const data = { message: '' }; + mockedFetch.mockImplementation(() => Response.json(data)); + const response = await users.deletePasskey('', ''); + + // Remove custom toString method on the objects to allow for clean data comparison. + delete response.toString; + + expect(response).toEqual(data); + }); test('test method updatePassword()', async () => { const data = { '\\$id': '5e5ea5c16897e',