diff --git a/CHANGELOG.md b/CHANGELOG.md index 934dd9a6..7900433b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change log +## 1.1.0 + +* Added: `Account.createRecoveryOTP` and `Account.updateRecoveryOTP` for code-based password recovery +* Updated: SDK now targets Appwrite 2.3 (`X-Appwrite-Response-Format: 2.3.0`) +* Updated: requests always send credentials now that dev keys are no longer supported + ## 1.0.0 * Stable: first stable release of the React Native SDK, out of beta diff --git a/README.md b/README.md index f804bf87..a9182ffe 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Appwrite React Native SDK ![License](https://img.shields.io/github/license/appwrite/sdk-for-react-native.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-2.2.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-2.3.0-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK targets Appwrite server version 2.2.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ. +**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ. Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the React Native SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs) diff --git a/docs/examples/account/create-recovery-otp.md b/docs/examples/account/create-recovery-otp.md new file mode 100644 index 00000000..0ece5dfa --- /dev/null +++ b/docs/examples/account/create-recovery-otp.md @@ -0,0 +1,16 @@ +```javascript +import { Client, Account } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.createRecoveryOTP({ + email: 'email@example.com', + phrase: false, // optional +}); + +console.log(result); +``` diff --git a/docs/examples/account/update-recovery-otp.md b/docs/examples/account/update-recovery-otp.md new file mode 100644 index 00000000..5bf3c2a0 --- /dev/null +++ b/docs/examples/account/update-recovery-otp.md @@ -0,0 +1,17 @@ +```javascript +import { Client, Account } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const result = await account.updateRecoveryOTP({ + userId: '', + secret: '', + password: 'password', +}); + +console.log(result); +``` diff --git a/package-lock.json b/package-lock.json index 0ff4366c..6458d5bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-native-appwrite", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-native-appwrite", - "version": "1.0.0", + "version": "1.1.0", "license": "BSD-3-Clause", "dependencies": { "expo-file-system": "18.*.*", diff --git a/package.json b/package.json index 64c433ee..5c05620f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-native-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": "1.0.0", + "version": "1.1.0", "license": "BSD-3-Clause", "main": "dist/cjs/sdk.js", "exports": { diff --git a/src/client.ts b/src/client.ts index 4a4fe33b..2a6c814d 100644 --- a/src/client.ts +++ b/src/client.ts @@ -208,8 +208,8 @@ class Client { 'x-sdk-name': 'React Native', 'x-sdk-platform': 'client', 'x-sdk-language': 'reactnative', - 'x-sdk-version': '1.0.0', - 'X-Appwrite-Response-Format': '2.2.0', + 'x-sdk-version': '1.1.0', + 'X-Appwrite-Response-Format': '2.3.0', }; /** @@ -787,12 +787,9 @@ class Client { const options: RequestInit = { method, headers, + credentials: 'include', }; - if (headers['X-Appwrite-Dev-Key'] === undefined) { - options.credentials = 'include'; - } - if (method === 'GET') { for (const [key, value] of Object.entries( Service.flatten(params), diff --git a/src/services/account.ts b/src/services/account.ts index 29083219..245ca446 100644 --- a/src/services/account.ts +++ b/src/services/account.ts @@ -2358,6 +2358,189 @@ export class Account extends Service { ); } + /** + * Use this endpoint to send a 6-digit password recovery code to the user's email address. Unlike [createRecovery](https://appwrite.io/docs/references/cloud/client-web/account#createRecovery), this method requires no redirect URL, which makes it suitable for mobile and desktop apps that cannot host a recovery page. Learn more about how to [complete the recovery process](https://appwrite.io/docs/references/cloud/client-web/account#updateRecoveryOTP). The code sent to the user's email address is valid for 15 minutes. + * + * Enable the **phrase** parameter to include a randomly generated security phrase in both the email and the response. Showing that phrase in your app lets the user confirm the email genuinely came from your request, which helps protect against phishing. + * + * + * @param {string} params.email - User email. + * @param {boolean} params.phrase - Toggle for security phrase. If enabled, email will be sent with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow. + * @throws {AppwriteException} + * @returns {Promise} + */ + createRecoveryOTP(params: { + email: string; + phrase?: boolean; + }): Promise; + /** + * Use this endpoint to send a 6-digit password recovery code to the user's email address. Unlike [createRecovery](https://appwrite.io/docs/references/cloud/client-web/account#createRecovery), this method requires no redirect URL, which makes it suitable for mobile and desktop apps that cannot host a recovery page. Learn more about how to [complete the recovery process](https://appwrite.io/docs/references/cloud/client-web/account#updateRecoveryOTP). The code sent to the user's email address is valid for 15 minutes. + * + * Enable the **phrase** parameter to include a randomly generated security phrase in both the email and the response. Showing that phrase in your app lets the user confirm the email genuinely came from your request, which helps protect against phishing. + * + * + * @param {string} email - User email. + * @param {boolean} phrase - Toggle for security phrase. If enabled, email will be sent with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + createRecoveryOTP(email: string, phrase?: boolean): Promise; + createRecoveryOTP( + paramsOrFirst: { email: string; phrase?: boolean } | string, + ...rest: [boolean?] + ): Promise { + let params: { email: string; phrase?: boolean }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + email: string; + phrase?: boolean; + }; + } else { + params = { + email: paramsOrFirst as string, + phrase: rest[0] as boolean, + }; + } + + const email = params.email; + const phrase = params.phrase; + + if (typeof email === 'undefined') { + throw new AppwriteException('Missing required parameter: "email"'); + } + + const apiPath = '/account/recovery/otp'; + const payload: Payload = {}; + + if (typeof email !== 'undefined') { + payload['email'] = email; + } + + if (typeof phrase !== 'undefined') { + payload['phrase'] = phrase; + } + + const uri = new URL(this.client.config.endpoint + apiPath); + return this.client.call( + 'post', + uri, + { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }, + payload, + ); + } + + /** + * Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + * + * + * @param {string} params.userId - User ID. + * @param {string} params.secret - Valid recovery OTP code. + * @param {string} params.password - New user password. Must be between 8 and 256 chars. + * @throws {AppwriteException} + * @returns {Promise} + */ + updateRecoveryOTP(params: { + userId: string; + secret: string; + password: string; + }): Promise; + /** + * Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + * + * + * @param {string} userId - User ID. + * @param {string} secret - Valid recovery OTP code. + * @param {string} password - New user password. Must be between 8 and 256 chars. + * @throws {AppwriteException} + * @returns {Promise} + * @deprecated Use the object parameter style method for a better developer experience. + */ + updateRecoveryOTP( + userId: string, + secret: string, + password: string, + ): Promise; + updateRecoveryOTP( + paramsOrFirst: + { userId: string; secret: string; password: string } | string, + ...rest: [string?, string?] + ): Promise { + let params: { userId: string; secret: string; password: string }; + + if ( + paramsOrFirst && + typeof paramsOrFirst === 'object' && + !Array.isArray(paramsOrFirst) + ) { + params = (paramsOrFirst || {}) as { + userId: string; + secret: string; + password: string; + }; + } else { + params = { + userId: paramsOrFirst as string, + secret: rest[0] as string, + password: rest[1] as string, + }; + } + + const userId = params.userId; + const secret = params.secret; + const password = params.password; + + if (typeof userId === 'undefined') { + throw new AppwriteException('Missing required parameter: "userId"'); + } + + if (typeof secret === 'undefined') { + throw new AppwriteException('Missing required parameter: "secret"'); + } + + if (typeof password === 'undefined') { + throw new AppwriteException( + 'Missing required parameter: "password"', + ); + } + + const apiPath = '/account/recovery/otp'; + const payload: Payload = {}; + + if (typeof userId !== 'undefined') { + payload['userId'] = userId; + } + + if (typeof secret !== 'undefined') { + payload['secret'] = secret; + } + + if (typeof password !== 'undefined') { + payload['password'] = password; + } + + const uri = new URL(this.client.config.endpoint + apiPath); + return this.client.call( + 'put', + uri, + { + 'X-Appwrite-Project': this.client.config.project, + 'content-type': 'application/json', + accept: 'application/json', + }, + payload, + ); + } + /** * Get the list of active sessions across different devices for the currently logged in user. * @@ -3259,7 +3442,7 @@ export class Account extends Service { } /** - * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. + * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. A session holds one push target per provider, so if one already exists this endpoint updates and returns that target instead of creating a second one, and a device that rotates its token is never notified twice. * * @param {string} params.targetId - Target 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.identifier - The target identifier (token, email, phone etc.) @@ -3273,7 +3456,7 @@ export class Account extends Service { providerId?: string; }): Promise; /** - * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. + * Use this endpoint to register a device for push notifications. Provide a target ID (custom or generated using ID.unique()), a device identifier (usually a device token), and optionally specify which provider should send notifications to this target. The target is automatically linked to the current session and includes device information like brand and model. A session holds one push target per provider, so if one already exists this endpoint updates and returns that target instead of creating a second one, and a device that rotates its token is never notified twice. * * @param {string} targetId - Target 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} identifier - The target identifier (token, email, phone etc.)