diff --git a/src/partials/auth-security.md b/src/partials/auth-security.md index 9094761cfd..97ae69f41f 100644 --- a/src/partials/auth-security.md +++ b/src/partials/auth-security.md @@ -12,9 +12,11 @@ Password dictionary can be enabled in the Auth service's **Security** tab on the # Password hashing {% #password-hashing %} Appwrite protects passwords by using the [Argon2](https://github.com/P-H-C/phc-winner-argon2) password-hashing algorithm. -Argon 2 is a resilient and secure password hashing algorithm, which is also the winner of the [Password Hashing Competition](https://www.password-hashing.net/) +Argon 2 is a resilient and secure password hashing algorithm, which is also the winner of the [Password Hashing Competition](https://www.password-hashing.net/). Appwrite combines Argon 2 with the use of techniques such as salting, adjustable work factors, and memory hardness to securely handle passwords. +If an user is imported into Appwrite with hash differnt than Argon2, the password will be re-hashed on first successful user's sign in. This ensures all passwords are stored as securely as possible. + # Personal data {% #personal-data %} Encourage passwords that are hard to guess by disallowing users to pick passwords that contain personal data. diff --git a/src/routes/docs/advanced/platform/+page.markdoc b/src/routes/docs/advanced/platform/+page.markdoc index e80b7470a4..82e69b5479 100644 --- a/src/routes/docs/advanced/platform/+page.markdoc +++ b/src/routes/docs/advanced/platform/+page.markdoc @@ -68,6 +68,6 @@ Configure custom domains and customize communication templates. Add a custom domain for your Appwrite APIs. {% /cards_item %} {% cards_item href="/docs/advanced/platform/message-templates" title="Message templates" %} -Create custom branding and messages when you communicate with users.. +Create custom branding and messages when you communicate with users. {% /cards_item %} {% /cards %} \ No newline at end of file diff --git a/src/routes/docs/apis/graphql/+page.markdoc b/src/routes/docs/apis/graphql/+page.markdoc index ed3c9e07fe..a80bb9acf1 100644 --- a/src/routes/docs/apis/graphql/+page.markdoc +++ b/src/routes/docs/apis/graphql/+page.markdoc @@ -106,8 +106,8 @@ If no errors occur, the array will not be present in the response. # Authentication {% #authentication %} GraphQL authenticates using Appwrite accounts and sessions. -Both accounts and sessions can be created with GraphQL using the `accountCreate`, `accountCreateEmailSession`, -`accountCreateAnonymousSession`, or `accountCreatePhoneSession` mutations. +Both accounts and sessions can be created with GraphQL using the `accountCreate`, `accountCreateEmailPasswordSession`, +`accountCreateAnonymousSession`, or `accountCreatePhoneToken` mutations. More information and examples of authenticating users can be found in the dedicated [authentication guide](/docs/products/auth). diff --git a/src/routes/docs/products/auth/+layout.svelte b/src/routes/docs/products/auth/+layout.svelte index f4f92c9cc9..465837c78a 100644 --- a/src/routes/docs/products/auth/+layout.svelte +++ b/src/routes/docs/products/auth/+layout.svelte @@ -61,6 +61,10 @@ label: 'Magic URL login', href: '/docs/products/auth/magic-url' }, + { + label: 'Email OTP login', + href: '/docs/products/auth/email-otp' + }, { label: 'OAuth 2 login', href: '/docs/products/auth/oauth2' @@ -73,6 +77,10 @@ label: 'JWT login', href: '/docs/products/auth/jwt' }, + { + label: 'Custom token login', + href: '/docs/products/auth/custom-token' + }, { label: 'Multi-factor authentication', href: '/docs/products/auth/mfa' diff --git a/src/routes/docs/products/auth/accounts/+page.markdoc b/src/routes/docs/products/auth/accounts/+page.markdoc index 378e4ed6d5..2b6959947c 100644 --- a/src/routes/docs/products/auth/accounts/+page.markdoc +++ b/src/routes/docs/products/auth/accounts/+page.markdoc @@ -12,17 +12,19 @@ Each user's account can also have their own preference object, which you can use # Signup and login {% #signup-login %} -You can signup and login a user with an account create through -[email password](/docs/products/auth/email-password), -[phone (SMS)](/docs/products/auth/phone-sms), -[Anonymous](/docs/products/auth/anonymous), -[magic URL](/docs/products/auth/magic-url), and -[OAuth 2](/docs/products/auth/oauth2) -authentication. +You can signup and login a user with an account created through +[Email and password](/docs/products/auth/email-password), +[OAuth 2](/docs/products/auth/oauth2), +[Magic URL](/docs/products/auth/magic-url), +[Email OTP](/docs/products/auth/email-otp), +[Phone (SMS)](/docs/products/auth/phone-sms), +and [Anonymous](/docs/products/auth/anonymous) authentications. + +You can also use [Custom token](/docs/products/auth/custom-token) to implement your own authentication flow, or integrate with any external provider. # Preferences {% #preferences %} -You can store user preferences on a user's account using Appwrite's [Update Preferences](/docs/references/cloud/client-web/account#updatePrefs) endpoint. You can store preferences such as theme, notification settings, or preferred language so they can be synced across multiple devices. +You can store user preferences on a user's account using Appwrite's [Update preferences](/docs/references/cloud/client-web/account#updatePrefs) endpoint. You can store preferences such as theme, notification settings, or preferred language so they can be synced across multiple devices. Preferences are stored as a key-value JSON object. The maximum allowed size for preferences is 64kB, and an error will be thrown if this limit is exceeded. @@ -108,7 +110,7 @@ mutation { ``` {% /multicode %} -After a user's preferences are updated, they can be retrieved using the [get account preferences](/docs/references/cloud/client-web/account#getPrefs) endpoint. +After a user's preferences are updated, they can be retrieved using the [Get preferences](/docs/references/cloud/client-web/account#getPrefs) endpoint. {% multicode %} ```js diff --git a/src/routes/docs/products/auth/anonymous/+page.markdoc b/src/routes/docs/products/auth/anonymous/+page.markdoc index 4596d6bf5d..cb8b9e61d0 100644 --- a/src/routes/docs/products/auth/anonymous/+page.markdoc +++ b/src/routes/docs/products/auth/anonymous/+page.markdoc @@ -10,7 +10,7 @@ Anonymous sessions allow you to implement **guest** users. Guest users let you s # Create anonymous session {% #createSession %} -Create an anonymous session with [Create Anonymous Session](/docs/references/cloud/client-web/account#createAnonymousSession) route. +Create an anonymous session with [Create Anonymous Session](/docs/references/cloud/client-web/account#createAnonymousSession) endpoint. {% multicode %} ```js @@ -78,7 +78,7 @@ mutation { # Attaching an account {% #attach-account %} -Anonymous users cannot sign back in. If the session expires, they move to another computer, or they clear their browser data, they won't be able to log in again. Remember to prompt the user to create an account to not lose their data. +Anonymous users cannot sign back in. If the session expires, they move to another computer, or they clear their browser data, they won't be able to log in again. Remember to prompt the user to claim an account to not lose their data. Create an account with any of these methods to transition from an anonymous session to a user account session. diff --git a/src/routes/docs/products/auth/custom-token/+page.markdoc b/src/routes/docs/products/auth/custom-token/+page.markdoc new file mode 100644 index 0000000000..b6b882f8c7 --- /dev/null +++ b/src/routes/docs/products/auth/custom-token/+page.markdoc @@ -0,0 +1,53 @@ +--- +layout: article +title: Custom token login +description: Limitless authentication flow in Appwrite. Find out how to implement custom authentication flow or connect to 3rd party authentication providers. +--- + +Tokens are short-lived secrets created by an [Appwrite Server SDK](/docs/sdks#server) that can be exchanged for session by a [Client SDK](/docs/sdks#client) to log in users. You may already be familiar with tokens if you checked out [Magic URL login](/docs/products/auth/magic-url), [Email OTP login](/docs/products/auth/email-otp) or [Phone (SMS) login](/docs/products/auth/phone-sms). + +Custom token allows you to use [Server SDK](/docs/sdks#server) to generate tokens for your own implementations. This allows you to code your own authentication methods using Appwrite Functions or your own backend. You could implement username and password sign-in, captcha-protected authentication, phone call auth, and much more. Custom tokens also allow you to skip authentication which is useful when you integrate Appwrite with external authenticaion providers such as Auth0, TypingDNA, or any provider trusted by your users. + +# Create custom token {% #create-custom-token %} + +Once you have your server endpoint prepared either in an Appwrite Function or a server integration, you can use the [Create token](/docs/references/cloud/server-node/users#createToken) endpoint of the [Users API](/docs/products/auth/users) to generate a token. + +```js +import { Client, Users } from "node-appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + .setKey(''); // Your secret API key + +const users = new Users(client); + +const token = await users.createToken('[USER_ID]'); + +console.log(token.secret); +``` + +The newly created token includes a secret which is 6 character long hexadecimal string. You can configure length of the secret and expiry when creating a token. + +If you are integrating with external authentication providers or implementing your own authentication, make sure to validate user authenticated properly before generating a token for them. + +If you are implementing token-based authentication flow, share the token secret with user over any channel of your choice instead of directly giving it to him in the response. + +If the client doesn't know the user's ID during authentication, we recommend to directly return user ID to the client as part of this step. If necessary, you can check if the user with an user ID exists first, and create a new user if needed. + +# Login {% #login %} + +Once the client receives a token secret, we can use it to authenticate the user in the application. Use the [Client SDK's](/docs/sdks#client) [Create session endpoint](/docs/references/cloud/client-web/account#createSession) to exchange the token secret for a valid session, which logs the user. +```js +import { Client, Account } from "appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +await account.createSession('[USER_ID]', '[SECRET]'); +``` + +When the session is successfully created, the session is stored in a persistent manner and you can now do requests as authorized user from the application. \ No newline at end of file diff --git a/src/routes/docs/products/auth/email-otp/+page.markdoc b/src/routes/docs/products/auth/email-otp/+page.markdoc new file mode 100644 index 0000000000..7d592c29ed --- /dev/null +++ b/src/routes/docs/products/auth/email-otp/+page.markdoc @@ -0,0 +1,243 @@ +--- +layout: article +title: Email OTP +description: Seamless sign in with Email OTP authentication in Appwrite. Learn how to provide simple and secure passwordless user accounts. +--- + +Email OTP (one-time password) authentication lets users create accounts using their email address and log in using a 6 digit code delivered to their email inbox. This method is similar to [Magic URL login](/docs/products/auth/magic-url), but can provide better user experience in some scenarios. + +{% info title="Email OTP vs Magic URL" %} +Email OTP sends an email with a 6 digit code that user needs to enter into the app, while Magic URL delivers a clickable button or a link to user's inbox. +Both allow passwordless login flows with different advantages. + +| Benefits of Email OTP | Downsides of Email OTP | +|--------------------------------------------------------------------|------------------------------------------| +| Doesn't require user to be signed into email inbox on the device | Expires quicker | +| Doesn't disturb application flow with a redirect | Requires more inputs from user | +| Doesn't require deep linking on mobile apps | | + +{% /info %} + +# Send email {% #send-email %} + +Email OTP authentication is done using a two-step authentication process. The authentication request is initiated from the client application and an email message is sent to the user's email inbox. The email will contain a 6-digit number the user can use to log in. + +Send an an email to initiate the authentication process. A **new account** will be created for this email if it has never been used before. + +{% multicode %} + +```js +import { Client, Account, ID } from "appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') + .setProject(''); + +const account = new Account(client); + +const sessionToken = await account.createEmailToken( + ID.unique(), + 'email@example.com' +); + +const userId = sessionToken.userId; +``` + +```dart +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://cloud.appwrite.io/v1') + .setProject(''); + +final account = Account(client); + +final sessionToken = await account.createEmailToken( + userId: ID.unique(), + email: 'email@example.com' +); + +final userId = sessionToken.userId; +``` + +```kotlin +import io.appwrite.Client +import io.appwrite.services.Account +import io.appwrite.ID + +val client = Client() + .setEndpoint("https://cloud.appwrite.io/v1") + .setProject(""); + +val account = Account(client); + +val sessionToken = account.createEmailToken( + userId = ID.unique(), + email = "email@example.com" +); + +val userId = sessionToken.userId; +``` + +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://cloud.appwrite.io/v1") + .setProject(""); + +let account = Account(client); + +let sessionToken = try await account.createEmailToken( + userId: ID.unique(), + email: "email@example.com" +); + +let userId = sessionToken.userId; +``` + +```graphql +mutation { + accountCreateEmailToken(userId: "unique()", email: "email@example.com") { + _id + userId + secret + expire + } +} +``` + +{% /multicode %} + +# Login {% #login %} + +After initiating the email OTP authentication process, the returned user ID and secret are used to authenticate the user. The user will use their 6-digit one-time password to log in to your app. + +{% multicode %} + +```js +import { Client, Account, ID } from "appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') + .setProject(''); + +const account = new Account(client); + +const session = await account.createSession( + userId, + '[SECRET]' +); +``` + +```dart +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://cloud.appwrite.io/v1') + .setProject(''); + +final account = Account(client); + +final session = await account.createSession( + userId: userId, + secret: '[SECRET]' +); +``` + +```kotlin +import io.appwrite.Client +import io.appwrite.services.Account +import io.appwrite.ID + +val client = Client() + .setEndpoint("https://cloud.appwrite.io/v1") + .setProject(""); + +val account = Account(client); + +val session = account.createSession( + userId = userId, + secret = "[SECRET]" +); +``` + +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://cloud.appwrite.io/v1") + .setProject(""); + +let account = Account(client); + +let session = try await account.createSession( + userId: userId, + secret: "[SECRET]" +); +``` + +```graphql +mutation { + accountcreateSession(userId: "[USER_ID]", secret: "[SECRET]") { + _id + userId + provider + expire + } +} +``` + +{% /multicode %} + +After the secret is verified, a session will be created. + +# Security phrase {% #security-phrase %} + +A security phrase is a randomly generated phrase provided on the login page, as well as inside Email OTP login email. Users must match the phrase on the login page with the phrase provided inside the email. Security phrases offer protection for various types of phishing and man-in-the-middle attacks. + +By default, security phrases are disabled. To enable a security phrase in Email OTP, enable it in first step of the authentication flow. + +{% multicode %} +```js +import { Client, Account, ID } from "appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const promise = account.createEmailToken( + ID.unique(), + 'email@example.com', + true + ); + +promise.then(function (response) { + console.log(response); // Success +}, function (error) { + console.log(error); // Failure +}); +``` + +```graphql +mutation { + accountCreateEmailToken( + userId: "ID.unique()", + email: "email@example.com", + phrase: true + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} +``` + +{% /multicode %} + +By enabling security phrase feature, you will recieve `phrase` in the response. You need to display this phrase to the user, and we recommend informing user what this phrase is and how to check it. When security phrase is enabled, email will also include a new section providing user with the security phrase. \ No newline at end of file diff --git a/src/routes/docs/products/auth/email-password/+page.markdoc b/src/routes/docs/products/auth/email-password/+page.markdoc index 2ae01695ff..cf3bfee480 100644 --- a/src/routes/docs/products/auth/email-password/+page.markdoc +++ b/src/routes/docs/products/auth/email-password/+page.markdoc @@ -8,7 +8,7 @@ Email and password login is the most commonly used authentication method. Appwri # Signup {% #sign-up %} -You can use the Appwrite Client SDKs to create an account using email and password. +You can use the Appwrite [Client SDKs](/docs/sdks#client) to create an account using email and password. ```js import { Client, Account, ID } from "appwrite"; @@ -19,7 +19,7 @@ const client = new Client() const account = new Account(client); -const promise = account.create('[USER_ID]', 'email@example.com', ''); +const promise = account.create(ID.unique(), 'email@example.com', 'password'); promise.then(function (response) { console.log(response); // Success @@ -30,48 +30,44 @@ promise.then(function (response) { Passwords are hashed with [Argon2](https://github.com/P-H-C/phc-winner-argon2), a resilient and secure password hashing algorithm. -# Verification {% #verification %} +# Login {% #login %} -After an account is created, it can be verified through the account create verification route. The user doesn't need to be verified to log in, but you can restrict resource access to verified users only using permissions through the `user([USER_ID], "verified")` role. - -First, send a verification email. Specify a redirect URL which users will be redirected to. The verification secrets will be appended as query parameters to the redirect URL. In this example, the redirect URL is `https://example.com/verify`. +After you've created your account, users can be logged in using the [Create Email Password Session](/docs/references/cloud/client-web/account#createEmailPasswordSession) endpoint. ```js import { Client, Account } from "appwrite"; const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID + .setProject(''); // Your project ID const account = new Account(client); -const promise = account.createVerification('https://example.com'); +const promise = account.createEmailPasswordSession('email@example.com', 'password'); promise.then(function (response) { - console.log(response); + console.log(response); // Success }, function (error) { - console.log(error); + console.log(error); // Failure }); ``` -Next, implement the verification page in your app. This page will parse the secrets passed in through the `userId` and `secret` query parameters. In this example, the code below will be found in the page served at `https://example.com/verify`. +# Verification {% #verification %} -Since the secrets are passed in through url params, it will be easiest to perform this step in the browser. +After an account is created, it can be verified through the account create verification endpoint. The user doesn't need to be verified to log in, but you can restrict resource access to verified users only using permissions through the `user([USER_ID], "verified")` role. + +First, send a verification email. Specify a redirect URL which users will be redirected to. The verification secrets will be appended as query parameters to the redirect URL. In this example, the redirect URL is `https://example.com/verify`. ```js import { Client, Account } from "appwrite"; const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject(''); // Your project ID + .setProject('') // Your project ID const account = new Account(client); -const urlParams = new URLSearchParams(window.location.search); -const secret = urlParams.get('secret'); -const userId = urlParams.get('userId'); - -const promise = account.updateVerification(userId, secret); +const promise = account.createVerification('https://example.com/verify'); promise.then(function (response) { console.log(response); @@ -80,9 +76,9 @@ promise.then(function (response) { }); ``` -# Login {% #login %} +Next, implement the verification page in your app. This page will parse the secrets passed in through the `userId` and `secret` query parameters. In this example, the code below will be found in the page served at `https://example.com/verify`. -After you've created your account, users can be logged in using the Create Email Session route. +Since the secrets are passed in through query params, it will be easiest to perform this step in the browser. ```js import { Client, Account } from "appwrite"; @@ -93,18 +89,22 @@ const client = new Client() const account = new Account(client); -const promise = account.createEmailSession('email@example.com', 'password'); +const urlParams = new URLSearchParams(window.location.search); +const secret = urlParams.get('secret'); +const userId = urlParams.get('userId'); + +const promise = account.updateVerification(userId, secret); promise.then(function (response) { - console.log(response); // Success + console.log(response); }, function (error) { - console.log(error); // Failure + console.log(error); }); ``` # Password Recovery {% #password-recovery %} -If a user forgets their password, they can initiate a password recovery flow to recover their password. The Create Password Recovery endpoint sends the user an email with a temporary secret key for password reset. When the user clicks the confirmation link, they are redirected back to the password reset URL with the secret key and email address values attached to the URL as query strings. +If a user forgets their password, they can initiate a password recovery flow to recover their password. The Create Password Recovery endpoint sends the user an email with a temporary secret key for password reset. When the user clicks the confirmation link, they are redirected back to the password reset URL with the secret key and user ID values attached to the URL as query strings. Only redirect URLs to domains added as a platform on your Appwrite Console will be accepted. URLs not added as a platform are rejected to protect against redirect attacks. @@ -115,7 +115,7 @@ const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID -const promise = account.createRecovery('email@example.com', 'https://example.com'); +const promise = account.createRecovery('email@example.com', 'https://example.com/recover-password'); promise.then(function (response) { console.log(response); // Success @@ -133,11 +133,15 @@ const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .setProject(''); // Your project ID +const urlParams = new URLSearchParams(window.location.search); +const secret = urlParams.get('secret'); +const userId = urlParams.get('userId'); + const promise = account.updateRecovery( - '[USER_ID]', - '[SECRET]', - 'password', - 'password' + userId, + secret, + 'new-password', + 'new-password' ); promise.then(function (response) { @@ -149,4 +153,4 @@ promise.then(function (response) { # Security {% #security %} -Appwrite's security first mindset goes beyond a securely implemented of authentication API. You can enable features like password dictionary, password history, and disallow personal data in passwords to encourage users to pick better passwords. By enabling these features, you protect user data and teach better password choices, which helps make the internet a safer place. +Appwrite's security first mindset goes beyond a securely implemented of [Authentication API](/docs/references/cloud/client-web/account). You can enable features like password dictionary, password history, and disallow personal data in passwords to encourage users to pick better passwords. By enabling these features, you protect user data and teach better password choices, which helps make the internet a safer place. [Learn more about security features](/docs/products/auth/security). diff --git a/src/routes/docs/products/auth/jwt/+page.markdoc b/src/routes/docs/products/auth/jwt/+page.markdoc index 24f1a181b1..25b62c2285 100644 --- a/src/routes/docs/products/auth/jwt/+page.markdoc +++ b/src/routes/docs/products/auth/jwt/+page.markdoc @@ -18,7 +18,7 @@ When you build backend APIs to extend Appwrite's functionality, these APIs shoul [JSON Web Tokens](https://jwt.io/introduction) (JWTs) are a secure means to transfer information or claims between two parties. JWTs act like temporary copies of the user's ID card that allow Appwrite's Server SDKs to access information on behalf of a user. -You need to create a session using the Client SDKs **before** generating a JWT. The JWT will be a stateless proof of claim for the identity of the authenticated user and expire after 15 minutes or when the session is deleted. +You need to create a session using the [Client SDKs](/docs/sdks#client) **before** generating a JWT. The JWT will be a stateless proof of claim for the identity of the authenticated user and expire after 15 minutes or when the session is deleted. You can generate a JWT like this on a [Client SDK](/docs/sdks#client). @@ -374,7 +374,7 @@ const { Client } = require('node-appwrite'); const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .setProject('') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setKey(''); // Your secret API key const databases = new sdk.Databases(client); @@ -391,7 +391,7 @@ use Appwrite\Client; $client = (new Client()) ->setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint ->setProject('') // Your project ID - ->setKey('98fd4...a2ad2'); // Your secret API key + ->setKey(''); // Your secret API key $databases = new Databases($client); @@ -409,7 +409,7 @@ client = Client() (client .set_endpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .set_project('') // Your project ID - .set_key('98fd4...a2ad2') // Your secret API key + .set_key('') // Your secret API key ) databases = Databases(client) @@ -428,7 +428,7 @@ include Appwrite client = Client.new .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint .set_project('') # Your project ID - .set_key('98fd4...a2ad2') # Your secret API key + .set_key('') # Your secret API key databases = Databases.new(client) @@ -444,7 +444,7 @@ import { Client } from "https://deno.land/x/appwrite/mod.ts"; let client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .setProject('') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setKey(''); // Your secret API key let databases = new sdk.Databases(client); @@ -460,7 +460,7 @@ import 'package:dart_appwrite/dart_appwrite.dart'; final client = Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint .setProject('') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setKey(''); // Your secret API key final databases = Databases(client); @@ -476,7 +476,7 @@ import io.appwrite.Client val client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint .setProject("") // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setKey(''); // Your secret API key val databases = Databases(client) @@ -492,7 +492,7 @@ import Appwrite let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint .setProject("") // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setKey(''); // Your secret API key let databases = Databases(client) @@ -510,7 +510,7 @@ using Appwrite.Models; var client = new Client() .SetEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint .SetProject("") // Your project ID - .SetKey('98fd4...a2ad2'); // Your secret API key + .SetKey(''); // Your secret API key var databases = new Databases(client); diff --git a/src/routes/docs/products/auth/labels/+page.markdoc b/src/routes/docs/products/auth/labels/+page.markdoc index ad0bb40811..f1d99d904f 100644 --- a/src/routes/docs/products/auth/labels/+page.markdoc +++ b/src/routes/docs/products/auth/labels/+page.markdoc @@ -12,8 +12,8 @@ const sdk = require('node-appwrite'); const client = new sdk.Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setProject('') // Your project ID + .setKey(''); // Your secret API key const users = new sdk.Users(client); @@ -38,8 +38,8 @@ $client = new Client(); $client .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setProject('') // Your project ID + .setKey(''); // Your secret API key $users = new Users($client); @@ -58,8 +58,8 @@ client = Client() (client .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint - .set_project('5df5acd0d48c2') # Your project ID - .set_key('98fd4...a2ad2') # Your secret API key + .set_project('') # Your project ID + .set_key('') # Your secret API key ) users = Users(client) @@ -76,8 +76,8 @@ include Appwrite client = Client.new .set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint - .set_project('5df5acd0d48c2') # Your project ID - .set_key('98fd4...a2ad2') # Your secret API key + .set_project('') # Your project ID + .set_key('') # Your secret API key users = Users.new(client) @@ -92,8 +92,8 @@ import * as sdk from "https://deno.land/x/appwrite/mod.ts"; let client = new sdk.Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setProject('') // Your project ID + .setKey(''); // Your secret API key let users = new sdk.Users(client); @@ -113,8 +113,8 @@ import 'package:dart_appwrite/dart_appwrite.dart'; final client = Client() .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID - .setKey('98fd4...a2ad2'); // Your secret API key + .setProject('') // Your project ID + .setKey(''); // Your secret API key final users = Users(client); @@ -130,8 +130,8 @@ import io.appwrite.services.Users val client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setKey("98fd4...a2ad2") // Your secret API key + .setProject("") // Your project ID + .setKey("") // Your secret API key val users = Users(client) @@ -146,8 +146,8 @@ import Appwrite let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID - .setKey("98fd4...a2ad2") // Your secret API key + .setProject("") // Your project ID + .setKey("") // Your secret API key let users = Users(client) @@ -162,8 +162,8 @@ using Appwrite; var client = new Client() .SetEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .SetProject("5df5acd0d48c2") // Your project ID - .SetKey("98fd4...a2ad2"); // Your secret API key + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key var users = new Users(client); diff --git a/src/routes/docs/products/auth/magic-url/+page.markdoc b/src/routes/docs/products/auth/magic-url/+page.markdoc index 74431482b7..c558e808c9 100644 --- a/src/routes/docs/products/auth/magic-url/+page.markdoc +++ b/src/routes/docs/products/auth/magic-url/+page.markdoc @@ -1,14 +1,26 @@ --- layout: article title: Magic URL login -description: Add magic URL to your authentication in Appwrite. Explore the convenience of passwordless login and email-based authentication using magic links." +description: Add magic URL to your authentication in Appwrite. Explore the convenience of passwordless login and email-based authentication using magic links. --- -Magic URL is a password-less way to authenticate users. When a user logs in by providing their email, they will receive an email with a "magic" link that contains a secret used to log in the user. The user can simply click the link to be logged in. +Magic URL is a password-less way to authenticate users. When a user logs in by providing their email, they will receive an email with a "magic" link that contains a secret used to log in the user. The user can simply click the link to be logged in. This method is similar to [Email OTP login](/docs/products/auth/email-otp), but can provide quicker sign-in in some scenarios. + +{% info title="Magic URL vs Email OTP" %} +Magic URL delivers a clickable button or a link to user's inbox, while Email OTP sends email with 6 digit code that user needs to enter into the app. +Both allow passwordless login flows with different advantages. + +| Benefits of Magic URL | Downsides of Magic URL | +|----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| Doesn't expire too quickly while remaining secure | Requires user to be signed into email inbox on the device | +| Doesn't require many inputs, only 1 click | Disturbs application flow with a redirect | +| | Requires deep linking when developing PWA or mobile apps | + +{% /info %} # Send email {% #init %} -Initialize the log in process with the [Create Magic URL Session](/docs/references/cloud/client-web/account#createMagicURLSession) route. If the email has never been used, a **new account is generated**, then the user will receive an email. If the email is already attached to an account, the **user ID is ignored** and the user will receive a link in their email. +Initialize the log in process with the [Create Magic URL Token](/docs/references/cloud/client-web/account#createMagicURLToken) endpoint. If the email has never been used, a **new account is generated**, then the user will receive an email. If the email is already attached to an account, the **user ID is ignored** and the user will receive a link in their email. {% multicode %} ```js @@ -20,7 +32,7 @@ const client = new Client() const account = new Account(client); -const promise = account.createMagicURLSession(ID.unique(), 'email@example.com'); +const promise = account.createMagicURLToken(ID.unique(), 'email@example.com'); promise.then(function (response) { console.log(response); // Success @@ -31,7 +43,7 @@ promise.then(function (response) { ```graphql mutation { - accountCreateMagicURLSession( + accountCreateMagicURLToken( userId: "ID.unique()", email: "email@example.com" ) { @@ -65,12 +77,12 @@ const urlParams = new URLSearchParams(window.location.search); const secret = urlParams.get('secret'); const userId = urlParams.get('userId'); -const user = await account.updateMagicURLSession(userId, secret); +const user = await account.createSession(userId, secret); ``` ```graphql mutation { - accountUpdateMagicURLSession( + accountCreateSession( userId: "unique()", secret: "[SECRET]" ) { @@ -83,3 +95,50 @@ mutation { } ``` {% /multicode %} + +# Security phrase {% #security-phrase %} + +A security phrase is a randomly generated phrase provided on the login page, as well as inside Magic URL login email. Users must match the phrase on the login page with the phrase provided inside the email. Security phrases offer protection for various types of phishing and man-in-the-middle attacks. + +By default, security phrases are disabled. To enable a security phrase in Magic URL, enable it in first step of the authentication flow. + + +{% multicode %} +```js +import { Client, Account, ID } from "appwrite"; + +const client = new Client() + .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const account = new Account(client); + +const promise = account.createMagicURLToken(ID.unique(), 'email@example.com', true); + +promise.then(function (response) { + console.log(response); // Success +}, function (error) { + console.log(error); // Failure +}); +``` + +```graphql +mutation { + accountCreateMagicURLToken( + userId: "ID.unique()", + email: "email@example.com", + phrase: true + ) { + _id + _createdAt + userId + secret + expire + phrase + } +} +``` + +{% /multicode %} + +By enabling security phrase feature, you will recieve `phrase` in the response. You need to display this phrase to the user, and we recommend informing user what this phrase is and how to check it. When security phrase is enabled, email will also include a new section providing user with the security phrase. \ No newline at end of file diff --git a/src/routes/docs/products/auth/oauth2/+page.markdoc b/src/routes/docs/products/auth/oauth2/+page.markdoc index d984076d17..8102024c42 100644 --- a/src/routes/docs/products/auth/oauth2/+page.markdoc +++ b/src/routes/docs/products/auth/oauth2/+page.markdoc @@ -6,11 +6,11 @@ description: Integrate OAuth2 authentication seamlessly with Appwrite. Learn how OAuth authentication allows users to log in using accounts from other popular services. This can be convenient for users because they can start using your app without creating a new account. It can also be more secure, because the user has one less password that could become vulnerable. -When using OAuth to authenticate, the authentication request is initiated from the client application. The user is then redirected to an OAuth 2 provider to complete the authentication step, and finally, the user is redirected back to the client application. +When using OAuth to authenticate, the authentication request is initiated from the client application. The user is then redirected to an OAuth 2 provider to complete the authentication step, and finally, the user is redirected back to your application. # Configure OAuth 2 login {% #configure %} -Before using OAuth 2 login, you need to enable and configure an OAuth 2 login provider. +Before using OAuth 2 login, you need to enable and configure an OAuth 2 provider. 1. Navigate to your Appwrite project. 2. Navigate to **Auth** > **Settings**. @@ -22,12 +22,11 @@ Before using OAuth 2 login, you need to enable and configure an OAuth 2 login pr # Initialize OAuth 2 login {% #init %} -To initialize the OAuth 2 login process, use the [Create OAuth 2 Session](/docs/references/cloud/client-web/account#createOAuth2Session) route. +To initialize the OAuth 2 login process, use the [Create OAuth 2 Session](/docs/references/cloud/client-web/account#createOAuth2Session) endpoint. {% tabs %} {% tabsitem #js title="Javascript" %} ```js -// Web SDK import { Client, Account } from "appwrite"; const client = new Client() @@ -37,13 +36,25 @@ const client = new Client() const account = new Account(client); // Go to OAuth provider login page -account.createOAuth2Session('amazon', [LINK_ON_SUCCESS], [LINK_ON_FAILURE]); +account.createOAuth2Session('github', '[LINK_ON_SUCCESS]', '[LINK_ON_FAILURE]', true); ``` + +Users will be redirected to the OAuth 2 provider's login page to log in. Once complete, your user will be redirected back to your app. + +In your web application, you need to finish OAuth authentication using [Create session](/docs/references/cloud/client-web/account#createSession) endpoint. Both user ID and token secret will be provided to you in the URL params. + +```js +const urlParams = new URLSearchParams(window.location.search); +const secret = urlParams.get('secret'); +const userId = urlParams.get('userId'); + +await account.createSession(userId, secret); +``` + {% /tabsitem %} {% tabsitem #flutter title="Flutter" %} ```dart -// Flutter SDK import 'package:appwrite/appwrite.dart'; final client = Client() @@ -53,8 +64,12 @@ final client = Client() final account = Account(client); // Go to OAuth provider login page -await account.createOAuth2Session(provider: 'amazon'); +await account.createOAuth2Session(provider: 'github'); ``` + +User will be redirected to the OAuth 2 provider's login page to log in. Once complete, your user will be redirected back to your app. + +You can optionally configure `success` or `failure` redirect links for having custom logic for handling those scenarios. {% /tabsitem %} {% tabsitem #android title="Android" %} @@ -82,8 +97,13 @@ val client = Client() val account = Account(client) // Go to OAuth provider login page -account.createOAuth2Session(provider = "amazon") +account.createOAuth2Session(provider = "github") ``` + +User will be redirected to the OAuth 2 provider's login page to log in. Once complete, your user will be redirected back to your app. + +You can optionally configure `success` or `failure` redirect links for having custom logic for handling those scenarios. + {% /tabsitem %} {% tabsitem #apple title="Apple" %} @@ -128,12 +148,13 @@ let account = Account(client) // Go to OAuth provider login page try await account.createOAuth2Session(provider: "amazon") ``` -{% /tabsitem %} -{% /tabs %} -You'll be redirected to the OAuth 2 provider's login page to log in. Once complete, your user will be redirected back to your app. +User will be redirected to the OAuth 2 provider's login page to log in. Once complete, your user will be redirected back to your app. -You can optionally configure `success` or `failure` redirect links on web to handle success and failure scenarios. +You can optionally configure `success` or `failure` redirect links for having custom logic for handling those scenarios. + +{% /tabsitem %} +{% /tabs %} # OAuth 2 profile {% #profile %} @@ -143,7 +164,6 @@ After creating an OAuth 2 session, you can fetch the session to get information {% multicode %} ```js -// Web SDK import { Client, Account } from "appwrite"; const client = new Client(); @@ -159,7 +179,6 @@ console.log(session.providerAccessToken); ``` ```dart -// Flutter SDK import 'package:appwrite/appwrite.dart'; final client = Client() @@ -179,7 +198,6 @@ print(session.providerAccessToken); ``` ```kotlin -// Android SDK import io.appwrite.Client import io.appwrite.services.Account @@ -200,7 +218,6 @@ print(session.providerAccessToken); ``` ```swift -// Apple SDK import Appwrite let client = Client() @@ -232,13 +249,12 @@ An OAuth 2 [session](/docs/references/cloud/models/session) will have the follow You can use the `providerAccessToken` to make requests to your OAuth 2 provider. Refer to the docs for the OAuth 2 provider you're using to learn about making API calls with the access token. -# OAuth 2 profile {% #profile %} +# Refreshing OAuth 2 tokens {% #refreshing-tokens %} -OAuth 2 sessions expire to protect from security risks. OAuth 2 sessions should be refreshed periodically, so access tokens don't expire. Check value of `providerAccessTokenExpiry` to know if the token is expired or is about to expire. Refreshing before every request might cause rate limit problems. You can do this by calling the [Update OAuth Session](/docs/references/cloud/client-web/account#updateSession) endpoint when ever your user visits your app. +OAuth 2 sessions expire to protect from security risks. OAuth 2 sessions should be refreshed periodically, so access tokens don't expire. Check value of `providerAccessTokenExpiry` to know if the token is expired or is about to expire. Refreshing before every request might cause rate limit problems. You can do this by calling the [Update session](/docs/references/cloud/client-web/account#updateSession) endpoint right before token expiry, or before first request after expiry. {% multicode %} ```js -// Web const promise = account.updateSession('[SESSION_ID]'); promise.then(function (response) { @@ -248,7 +264,6 @@ promise.then(function (response) { }); ``` ```dart -// Flutter import 'package:appwrite/appwrite.dart'; final client = Client() @@ -262,7 +277,6 @@ final result = await account.updateSession( ); ``` ```kotlin -// Android import io.appwrite.Client import io.appwrite.services.Account @@ -277,7 +291,6 @@ val response = account.updateSession( ); ``` ```swift -// Apple import Appwrite let client = Client() diff --git a/src/routes/docs/products/auth/phone-sms/+page.markdoc b/src/routes/docs/products/auth/phone-sms/+page.markdoc index b3f840a37f..f878610312 100644 --- a/src/routes/docs/products/auth/phone-sms/+page.markdoc +++ b/src/routes/docs/products/auth/phone-sms/+page.markdoc @@ -8,7 +8,7 @@ Phone authentication lets users create accounts using their phone numbers and lo # Send SMS message {% #init %} -Phone authentication is done using a two-step authentication process. When using phone authentication, the authentication request is initiated from the client application and an SMS message is sent to the user's phone. The SMS message will contain a secret the user can use to log in. +Phone authentication is done using a two-step authentication process. When using phone authentication, the authentication request is initiated from the client application and an SMS message is sent to the user's phone. The SMS message will contain a 6-digit number the user can use to log in. Send an SMS message to initiate the authentication process. A **new account** will be created for this phone number if it has never been used before. @@ -23,7 +23,7 @@ const client = new Client() const account = new Account(client); -const sessionToken = await account.createPhoneSession( +const sessionToken = await account.createPhoneToken( ID.unique(), '+14255550123' ); @@ -40,7 +40,7 @@ final client = Client() final account = Account(client); -final sessionToken = await account.createPhoneSession( +final sessionToken = await account.createPhoneToken( userId: ID.unique(), phone: '+14255550123' ); @@ -59,7 +59,7 @@ val client = Client() val account = Account(client); -val sessionToken = account.createPhoneSession( +val sessionToken = account.createPhoneToken( userId = ID.unique(), phone = "+14255550123" ); @@ -76,7 +76,7 @@ let client = Client() let account = Account(client); -let sessionToken = try await account.createPhoneSession( +let sessionToken = try await account.createPhoneToken( userId: ID.unique(), phone: "+14255550123" ); @@ -86,7 +86,7 @@ let userId = sessionToken.userId; ```graphql mutation { - accountCreatePhoneSession(userId: "unique()", phone: "+14255550123") { + accountCreatePhoneToken(userId: "unique()", phone: "+14255550123") { _id userId secret @@ -99,7 +99,7 @@ mutation { # Login {% #login %} -After initiating the phone authentication process, the returned user ID and secret are used to confirm the user. The secret will usually be a 6-digit number in the SMS message sent to the user. +After initiating the phone authentication process, the returned user ID and secret are used to authenticate the user. The secret will be a 6-digit number in the SMS message sent to the user. {% multicode %} @@ -112,7 +112,7 @@ const client = new Client() const account = new Account(client); -const session = await account.updatePhoneSession( +const session = await account.createSession( userId, '[SECRET]' ); @@ -127,7 +127,7 @@ final client = Client() final account = Account(client); -final session = await account.updatePhoneSession( +final session = await account.createSession( userId: userId, secret: '[SECRET]' ); @@ -144,7 +144,7 @@ val client = Client() val account = Account(client); -val session = account.updatePhoneSession( +val session = account.createSession( userId = userId, secret = "[SECRET]" ); @@ -159,7 +159,7 @@ let client = Client() let account = Account(client); -let session = try await account.updatePhoneSession( +let session = try await account.createSession( userId: userId, secret: "[SECRET]" ); @@ -167,7 +167,7 @@ let session = try await account.updatePhoneSession( ```graphql mutation { - accountUpdatePhoneSession(userId: "[USER_ID]", secret: "[SECRET]") { + accountcreateSession(userId: "[USER_ID]", secret: "[SECRET]") { _id userId provider diff --git a/src/routes/docs/products/auth/quick-start/+page.markdoc b/src/routes/docs/products/auth/quick-start/+page.markdoc index 341767b3ed..e5c6f5c823 100644 --- a/src/routes/docs/products/auth/quick-start/+page.markdoc +++ b/src/routes/docs/products/auth/quick-start/+page.markdoc @@ -20,7 +20,7 @@ const client = new Client() const account = new Account(client); -const promise = account.create('[USER_ID]', 'email@example.com', ''); +const promise = account.create(ID.unique(), 'email@example.com', 'password'); promise.then(function (response) { console.log(response); // Success @@ -89,7 +89,7 @@ mutation { # Login {% #login %} -After you've created your account, users can be logged in using the [Create Email Session](/docs/references/cloud/client-web/account#createEmailSession) route. +After you've created your account, users can be logged in using the [Create Email Password Session](/docs/references/cloud/client-web/account#createEmailPasswordSession) endpoint. {% multicode %} ```js @@ -101,7 +101,7 @@ const client = new Client() const account = new Account(client); -const promise = account.createEmailSession('email@example.com', 'password'); +const promise = account.createEmailPasswordSession('email@example.com', 'password'); promise.then(function (response) { console.log(response); // Success @@ -119,7 +119,7 @@ final client = Client() final account = Account(client); -final session = await account.createEmailSession( +final session = await account.createEmailPasswordSession( email: 'email@example.com', password: 'password' ); @@ -135,7 +135,7 @@ val client = Client() val account = Account(client) -val session = account.createEmailSession( +val session = account.createEmailPasswordSession( email = "email@example.com", password = "password" ) @@ -150,7 +150,7 @@ let client = Client() let account = Account(client) -let session = try await account.createEmailSession( +let session = try await account.createEmailPasswordSession( email: "email@example.com", password: "password" ) @@ -158,7 +158,7 @@ let session = try await account.createEmailSession( ```graphql mutation { - accountCreateEmailSession(email: "email@example.com", password: "password") { + accountCreateEmailPasswordSession(email: "email@example.com", password: "password") { _id userId provider @@ -169,11 +169,12 @@ mutation { {% /multicode %} # More ways to authenticate {% #more-ways-to-authenticate %} -You can signup and login a user with an account create through -[email password](/docs/products/auth/email-password), -[phone (SMS)](/docs/products/auth/phone-sms), -[Anonymous](/docs/products/auth/anonymous) -[magic URL](/docs/products/auth/magic-url), and -[OAuth 2](/docs/products/auth/oauth2) -authentication. - +You can signup and login a user with an account created through +[Email and password](/docs/products/auth/email-password), +[OAuth 2](/docs/products/auth/oauth2), +[Magic URL](/docs/products/auth/magic-url), +[Email OTP](/docs/products/auth/email-otp), +[Phone (SMS)](/docs/products/auth/phone-sms), +and [Anonymous](/docs/products/auth/anonymous) authentications. + +You can also use [Custom token](/docs/products/auth/custom-token) to implement your own authentication flow, or integrate with any external provider. \ No newline at end of file diff --git a/src/routes/docs/products/auth/security/+page.markdoc b/src/routes/docs/products/auth/security/+page.markdoc index 5fc6fed093..9568f923a4 100644 --- a/src/routes/docs/products/auth/security/+page.markdoc +++ b/src/routes/docs/products/auth/security/+page.markdoc @@ -16,15 +16,27 @@ Only keep user sessions active as long as needed and maintain exactly **one** in | {% width=70 %} | Framework {% width=120 %} | Storage method | |:----------------------------------------------------------------------------------------------------:|:---:|:----------------------------------------------------------------------------------------------------------------------:| -| {% only_dark %}{% icon_image src="/images/platforms/dark/javascript.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/javascript.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Web | Uses a secure session cookie and falls back to local storage when a session cookie is not available. | -| {% only_dark %}{% icon_image src="/images/platforms/dark/flutter.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/flutter.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Flutter | Uses a session cookie stored in Application Documents through the **path_provider** package. | -| {% only_dark %}{% icon_image src="/images/platforms/dark/apple.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/apple.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Apple | Uses a session cookie stored in **UserDefaults**. | -| {% only_dark %}{% icon_image src="/images/platforms/dark/android.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/android.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Android | Uses a session cookie stored in **SharedPreferences**. | +| {% only_dark %}{% icon_image src="/images/platforms/dark/javascript.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/javascript.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Web | Uses a secure cookie and falls back to local storage when a session cookie is not available. | +| {% only_dark %}{% icon_image src="/images/platforms/dark/flutter.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/flutter.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Flutter | Uses a cookie stored in Application Documents through the **path_provider** package. | +| {% only_dark %}{% icon_image src="/images/platforms/dark/apple.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/apple.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Apple | Uses a cookie stored in **UserDefaults**. | +| {% only_dark %}{% icon_image src="/images/platforms/dark/android.svg" alt="Javascript logo" size="m" /%}{% /only_dark %}{% only_light %}{% icon_image src="/images/platforms/android.svg" alt="Javascript logo" size="m" /%}{% /only_light %} | Android | Uses a cookie stored in **SharedPreferences**. | + +# Users limit {% #users-limit %} + +You can limit amount of users to be allowed to sign up in your application. Once the limit is reached, all new sign-ups will be blocked. This is useful for beta testing or only allowing moderated sign-ups into your application. + +To set users limit, head to the Appwrite Console > **Authentication** > **Security**. By default it's unlimited and maximum configurable limit is 10,000. # Session limits {% #session-limits %} -In Appwrite versions 1.2 and above, you can limit the number of active sessions created per user to prevent the accumulation of unused but active sessions. New sessions created by the same user past the session limit delete the oldest session. +You can limit the number of active sessions created per user to prevent the accumulation of unused but active sessions. The oldest session is deleted when creating sessions beyond the session limit. + +You can change the session limit by heading to the Appwrite Console > **Authentication** > **Security**. The default session limit is 10 with a maximum configurable limit of 100. + +# Session length {% #session-length %} + +Every session must expire for security reasons. By default, sessions made in Appwrite will expire after 1 year. You can configure the expiration by heading to the Appwrite Console > **Authentication** > **Security**. You can not make it any longer than 1 year, and you can make it as short as you want. -You can change the session limit in the **Security** tab of the Auth Service in your Appwrite Console. The default session limit is 10 with a maximum configurable limit of 100. +When using short session lengths such as 5 minutes, we recommend sending request to [Update session](/docs/references/cloud/client-web/account#updateSession) endpoint everytime user performs an action. By doing that, user's session expiry is extended to session length. This means the session is invalidated within few minutes after user stops interacting with the application, and the user is logged out. # Permissions {% #permissions %} diff --git a/src/routes/docs/products/auth/teams/+page.markdoc b/src/routes/docs/products/auth/teams/+page.markdoc index fc0c9f7d89..ce7fd7262a 100644 --- a/src/routes/docs/products/auth/teams/+page.markdoc +++ b/src/routes/docs/products/auth/teams/+page.markdoc @@ -8,7 +8,7 @@ Teams are a good way to allow users to share access to resources. For example, in a todo app, a user can [create a team](/docs/references/cloud/client-web/teams#create) for one of their todo lists and [invite another user](/docs/references/cloud/client-web/teams#createMembership) to the team to grant the other user access. You can further give special rights to parts of a team using team roles. -The invited user can [accept the invitation](/docs/references/cloud/client-web/teams#updateMembershipStatus) to gain access. If the user's ever removed from the team, they'll lose access again. +The invited user can [accept the invitation](/docs/references/cloud/client-web/teams#updateMembershipStatus) to gain access. If the user is ever removed from the team, they'll lose access again. # Create team {% #create %} For example, we can create a team called `teachers` with roles `maths`, `sciences`, `arts`, and `literature`. @@ -25,7 +25,7 @@ const teams = new Teams(client); client .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID + .setProject('') // Your project ID ; const promise = teams.create( @@ -49,7 +49,7 @@ void main() { // Init SDK client .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID + .setProject('') // Your project ID ; Future result = teams.create( teamId: 'teachers', @@ -70,7 +70,7 @@ import Appwrite let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID + .setProject("") // Your project ID let teams = Teams(client) @@ -86,7 +86,7 @@ import io.appwrite.services.Teams val client = Client(context) .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID + .setProject("") // Your project ID val teams = Teams(client) @@ -113,7 +113,7 @@ const teams = new Teams(client); client .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID + .setProject('') // Your project ID ; const promise = teams.createMembership( @@ -137,7 +137,7 @@ void main() { // Init SDK client .setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint - .setProject('5df5acd0d48c2') // Your project ID + .setProject('') // Your project ID ; Future result = teams.createMembership( teamId: 'teachers', @@ -158,7 +158,7 @@ import Appwrite let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID + .setProject("") // Your project ID let teams = Teams(client) @@ -174,7 +174,7 @@ import io.appwrite.services.Teams val client = Client(context) .setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint - .setProject("5df5acd0d48c2") // Your project ID + .setProject("") // Your project ID val teams = Teams(client) diff --git a/src/routes/docs/products/auth/users/+page.markdoc b/src/routes/docs/products/auth/users/+page.markdoc index c71865219c..eba37ea583 100644 --- a/src/routes/docs/products/auth/users/+page.markdoc +++ b/src/routes/docs/products/auth/users/+page.markdoc @@ -10,7 +10,7 @@ If you need to act on behalf of users through an Appwrite Function or your own b {% partial file="account-vs-user.md" /%} -The users API can be used to create users, import users, update user info, get user audit logs, and remove users. +The [Users API](/docs/references/cloud/client-web/users) can be used to create users, import and export users, update user info, get user audit logs, and remove users. {% arrow_link href="/docs/references/cloud/server-nodejs/users" %} Learn more in the Users API references diff --git a/src/routes/docs/quick-starts/android/+page.markdoc b/src/routes/docs/quick-starts/android/+page.markdoc index bb3e718a40..44581291a8 100644 --- a/src/routes/docs/quick-starts/android/+page.markdoc +++ b/src/routes/docs/quick-starts/android/+page.markdoc @@ -112,7 +112,7 @@ object Appwrite { email: String, password: String, ): Session { - return account.createEmailSession( + return account.createEmailPasswordSession( email, password, ) diff --git a/src/routes/docs/quick-starts/angular/+page.markdoc b/src/routes/docs/quick-starts/angular/+page.markdoc index c23bdd5463..6bccde590f 100644 --- a/src/routes/docs/quick-starts/angular/+page.markdoc +++ b/src/routes/docs/quick-starts/angular/+page.markdoc @@ -82,7 +82,7 @@ export { ID } from 'appwrite'; ``` {% /section %} {% section #step-5 step=5 title="Create a login page" %} -First, add imports for the `FormsModule` from Angular to handle the login form.. +First, add imports for the `FormsModule` from Angular to handle the login form. ```ts import { FormsModule } from '@angular/forms'; @@ -144,7 +144,7 @@ export class AppComponent { name: string = ''; async login(email: string, password: string) { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); this.loggedInUser = await account.get(); } diff --git a/src/routes/docs/quick-starts/apple/+page.markdoc b/src/routes/docs/quick-starts/apple/+page.markdoc index 5a95a15823..830b684658 100644 --- a/src/routes/docs/quick-starts/apple/+page.markdoc +++ b/src/routes/docs/quick-starts/apple/+page.markdoc @@ -131,7 +131,7 @@ class Appwrite { _ email: String, _ password: String ) async throws -> Session { - try await account.createEmailSession( + try await account.createEmailPasswordSession( email: email, password: password ) diff --git a/src/routes/docs/quick-starts/flutter/+page.markdoc b/src/routes/docs/quick-starts/flutter/+page.markdoc index 193b574d48..349478cf37 100644 --- a/src/routes/docs/quick-starts/flutter/+page.markdoc +++ b/src/routes/docs/quick-starts/flutter/+page.markdoc @@ -217,7 +217,7 @@ class MyFormState extends State { final TextEditingController nameController = TextEditingController(); Future login(String email, String password) async { - await widget.account.createEmailSession(email: email, password: password); + await widget.account.createEmailPasswordSession(email: email, password: password); final user = await widget.account.get(); setState(() { loggedInUser = user; diff --git a/src/routes/docs/quick-starts/nextjs/+page.markdoc b/src/routes/docs/quick-starts/nextjs/+page.markdoc index 78f022c5f4..fb126b6377 100644 --- a/src/routes/docs/quick-starts/nextjs/+page.markdoc +++ b/src/routes/docs/quick-starts/nextjs/+page.markdoc @@ -86,7 +86,7 @@ const LoginPage = () => { const [name, setName] = useState(""); const login = async (email, password) => { - const session = await account.createEmailSession(email, password); + const session = await account.createEmailPasswordSession(email, password); setLoggedInUser(await account.get()); }; diff --git a/src/routes/docs/quick-starts/nuxt/+page.markdoc b/src/routes/docs/quick-starts/nuxt/+page.markdoc index 1d43a7e82f..ba71f59181 100644 --- a/src/routes/docs/quick-starts/nuxt/+page.markdoc +++ b/src/routes/docs/quick-starts/nuxt/+page.markdoc @@ -88,7 +88,7 @@ const password = ref(''); const name = ref(''); const login = async (email, password) => { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); loggedInUser.value = await account.get(); }; diff --git a/src/routes/docs/quick-starts/react/+page.markdoc b/src/routes/docs/quick-starts/react/+page.markdoc index 02debc7b43..ce32a9976a 100644 --- a/src/routes/docs/quick-starts/react/+page.markdoc +++ b/src/routes/docs/quick-starts/react/+page.markdoc @@ -85,7 +85,7 @@ const App = () => { const [name, setName] = useState(''); async function login(email, password) { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); setLoggedInUser(await account.get()); } diff --git a/src/routes/docs/quick-starts/sveltekit/+page.markdoc b/src/routes/docs/quick-starts/sveltekit/+page.markdoc index dc02891eea..e5e4d8a3fb 100644 --- a/src/routes/docs/quick-starts/sveltekit/+page.markdoc +++ b/src/routes/docs/quick-starts/sveltekit/+page.markdoc @@ -80,7 +80,7 @@ Replace the contents of `src/routes/+page.svelte` with the following code. let loggedInUser = null; async function login(email, password) { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); loggedInUser = await account.get(); } diff --git a/src/routes/docs/quick-starts/vue/+page.markdoc b/src/routes/docs/quick-starts/vue/+page.markdoc index 0d276d5ae1..d4c0c0464d 100644 --- a/src/routes/docs/quick-starts/vue/+page.markdoc +++ b/src/routes/docs/quick-starts/vue/+page.markdoc @@ -107,7 +107,7 @@ const password = ref(''); const name = ref(''); const login = async (email, password) => { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); loggedInUser.value = await account.get(); }; diff --git a/src/routes/docs/references/[version]/[platform]/[service]/descriptions/account.md b/src/routes/docs/references/[version]/[platform]/[service]/descriptions/account.md index 2ad8ccf219..afabaf9b24 100644 --- a/src/routes/docs/references/[version]/[platform]/[service]/descriptions/account.md +++ b/src/routes/docs/references/[version]/[platform]/[service]/descriptions/account.md @@ -1,5 +1,5 @@ The Account service allows you to authenticate and manage a user account. You can use the account service to update user information, retrieve the user sessions across different devices, and fetch the user security logs with his or her recent activity. -Register new user accounts with the [Create Account](/docs/references/cloud/client-web/account#create), [Create Magic URL session](/docs/references/cloud/client-web/account#createMagicURLSession), or [Create Phone session](/docs/references/cloud/client-web/account#createPhoneSession) endpoint. You can authenticate the user account by using multiple sign-in methods available. Once the user is authenticated, a new session object will be created to allow the user to access his or her private data and settings. +Register new user accounts with the [Create Account](/docs/references/cloud/client-web/account#create), [Create Magic URL session](/docs/references/cloud/client-web/account#createMagicURLSession), or [Create Phone session](/docs/references/cloud/client-web/account#createPhoneToken) endpoint. You can authenticate the user account by using multiple sign-in methods available. Once the user is authenticated, a new session object will be created to allow the user to access his or her private data and settings. This service also exposes an endpoint to save and read the [user preferences](/docs/references/cloud/client-web/account#updatePrefs) as a key-value object. This feature is handy if you want to allow extra customization in your app. Common usage for this feature may include saving the user's preferred locale, timezone, or custom app theme. diff --git a/src/routes/docs/tutorials/android/step-4/+page.markdoc b/src/routes/docs/tutorials/android/step-4/+page.markdoc index 704ce2546e..4d3f00aa16 100644 --- a/src/routes/docs/tutorials/android/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/android/step-4/+page.markdoc @@ -34,7 +34,7 @@ class AccountService(client: Client) { suspend fun login(email: String, password: String): User>? { return try { - account.createEmailSession(email, password) + account.createEmailPasswordSession(email, password) getLoggedIn() } catch (e: AppwriteException) { null diff --git a/src/routes/docs/tutorials/nuxt/step-4/+page.markdoc b/src/routes/docs/tutorials/nuxt/step-4/+page.markdoc index 8c17094444..c24eb03f39 100644 --- a/src/routes/docs/tutorials/nuxt/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/nuxt/step-4/+page.markdoc @@ -39,7 +39,7 @@ export const useUserSession = () => { }; const login = async (email: string, password: string): Promise => { - const authUser = await account.createEmailSession(email, password); // Open user session in Appwrite + const authUser = await account.createEmailPasswordSession(email, password); // Open user session in Appwrite current.value = authUser; // Pass user data to current ref navigateTo("/"); }; diff --git a/src/routes/docs/tutorials/react/step-4/+page.markdoc b/src/routes/docs/tutorials/react/step-4/+page.markdoc index af576f745b..3828215ae2 100644 --- a/src/routes/docs/tutorials/react/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/react/step-4/+page.markdoc @@ -27,7 +27,7 @@ export function UserProvider(props) { const [user, setUser] = useState(null); async function login(email, password) { - const loggedIn = await account.createEmailSession(email, password); + const loggedIn = await account.createEmailPasswordSession(email, password); setUser(loggedIn); } diff --git a/src/routes/docs/tutorials/sveltekit-auth/step-5/+page.markdoc b/src/routes/docs/tutorials/sveltekit-auth/step-5/+page.markdoc index 289773dabd..49154f6ab0 100644 --- a/src/routes/docs/tutorials/sveltekit-auth/step-5/+page.markdoc +++ b/src/routes/docs/tutorials/sveltekit-auth/step-5/+page.markdoc @@ -52,7 +52,7 @@ Now we just need to create a form to let the user input sign in data. } try { - await appwrite.account.createEmailSession(email, password); + await appwrite.account.createEmailPasswordSession(email, password); await invalidateAll(); } catch (e) { formError = /** @type {import('appwrite').AppwriteException} */ (e).message; diff --git a/src/routes/docs/tutorials/sveltekit-auth/step-6/+page.markdoc b/src/routes/docs/tutorials/sveltekit-auth/step-6/+page.markdoc index 185b25174b..1f9c16fc24 100644 --- a/src/routes/docs/tutorials/sveltekit-auth/step-6/+page.markdoc +++ b/src/routes/docs/tutorials/sveltekit-auth/step-6/+page.markdoc @@ -37,7 +37,7 @@ For signup, you can copy the login `+page.js` and `+page.svelte` files into `src try { await appwrite.account.create(ID.unique(), email, password, name); - await appwrite.account.createEmailSession(email, password); + await appwrite.account.createEmailPasswordSession(email, password); await invalidateAll(); } catch (e) { formError = /** @type {import('appwrite').AppwriteException} */ (e).message; diff --git a/src/routes/docs/tutorials/sveltekit/step-4/+page.markdoc b/src/routes/docs/tutorials/sveltekit/step-4/+page.markdoc index d7b9686e08..8c0b947590 100644 --- a/src/routes/docs/tutorials/sveltekit/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/sveltekit/step-4/+page.markdoc @@ -43,7 +43,7 @@ const createUser = () => { async function login(email, password) { if (!isBrowser) return; - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); await init(); goto('/'); // Redirect to home page after login } diff --git a/src/routes/docs/tutorials/vue/step-4/+page.markdoc b/src/routes/docs/tutorials/vue/step-4/+page.markdoc index ea0941f10f..a73548b43b 100644 --- a/src/routes/docs/tutorials/vue/step-4/+page.markdoc +++ b/src/routes/docs/tutorials/vue/step-4/+page.markdoc @@ -31,7 +31,7 @@ export const user = reactive({ await this.login(email, password); }, async login(email, password) { - await account.createEmailSession(email, password); + await account.createEmailPasswordSession(email, password); window.location.href = "/"; // Redirect to home page }, async logout() {