Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: New auth docs #566

Merged
merged 12 commits into from
Feb 4, 2024
4 changes: 3 additions & 1 deletion src/partials/auth-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions src/routes/docs/apis/graphql/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
8 changes: 8 additions & 0 deletions src/routes/docs/products/auth/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -73,6 +77,10 @@
label: 'JWT login',
href: '/docs/products/auth/jwt'
},
{
label: 'Custom token login',
href: '/docs/products/auth/custom-token'
}
]
},
{
Expand Down
20 changes: 11 additions & 9 deletions src/routes/docs/products/auth/accounts/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/routes/docs/products/auth/anonymous/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.

Expand Down
54 changes: 54 additions & 0 deletions src/routes/docs/products/auth/custom-token/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
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 server that can be exchanged for session by client. 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).
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

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 custom backend. You could implement nick&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.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

# Create custom token {% #init %}
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

Once you have your server endpoint prepared either in Appwrite Function or custom backend, you can use [Create token](/docs/references/cloud/server-node/users#createToken) endpoint of Users API to generate a token.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

```js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need more languages?

import { Client, Users } from "node-appwrite";

const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<PROJECT_ID>'); // Your project ID
.setKey('<API_KEY>'); // Your secret API key

const users = new Users(client);

const token = await users.createToken('[USER_ID]');

console.log(token.secret);
```

Token that got created includes a secret which is 6 character long hexadecimal string. You can configure length of the secret and expiry when creating a token.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

If you are integrating with external provider or implementented your own, make sure to validate user authenticated properly before generating a token for him.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

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.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

If the client doesn't know his user ID during authentication, we recommend to directly return user ID to client as part of this step. If nessessary, you may also want to create or find user in your code based on specific defails you are provided in your authentication flow.
gewenyu99 marked this conversation as resolved.
Show resolved Hide resolved

# Login {% #login %}

Once client recieves a token secret, we can use it to authenticate the user in the application. Using [Client SDKs](/docs/sdks#client) endpoint [Create session](/docs/references/cloud/client-web/account#createSession) you can exchange token secret for a valid session.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

```js
import { Client, Account } from "appwrite";

const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<PROJECT_ID>'); // 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.
240 changes: 240 additions & 0 deletions src/routes/docs/products/auth/email-otp/+page.markdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
---
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 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.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

{% info title="Email OTP vs Magic URL" %}
Email OTP sends email with 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.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

#### Benefits of Email OTP
gewenyu99 marked this conversation as resolved.
Show resolved Hide resolved
- Doesn't require user to be signed into email inbox on the device
- Doesn't disturb application flow with a redirect
- Doesn't require deep linking when developing PWA or mobile apps
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

#### Downsides of Email OTP
gewenyu99 marked this conversation as resolved.
Show resolved Hide resolved
- Expires quicker
- Requires more inputs from user
{% /info %}

# Send email {% #init %}
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

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 phone number if it has never been used before.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

{% multicode %}

```js
import { Client, Account, ID } from "appwrite";

const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject('<PROJECT_ID>');

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('<PROJECT_ID>');

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("<PROJECT_ID>");

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("<PROJECT_ID>");

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 secret will be a 6-digit number in the email sent to the user's email inbox.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

{% multicode %}

```js
import { Client, Account, ID } from "appwrite";

const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject('<PROJECT_ID>');

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('<PROJECT_ID>');

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("<PROJECT_ID>");

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("<PROJECT_ID>");

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 sign-in page, as well as inside Email OTP login email. Verifying those phrases match is a prevention for numerous phishing and man-in-the-middle attacks.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

By default, security phrases are disabled. To enable a security prhase in Email OTP, enable it in first step of authentication flow.
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

{% multicode %}
```js
import { Client, Account, ID } from "appwrite";

const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<PROJECT_ID>'); // Your project ID

const account = new Account(client);

const promise = account.createEmailToken(ID.unique(), 'email@example.com', true);
Meldiron marked this conversation as resolved.
Show resolved Hide resolved

promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No kotlin dart swift? ;)


```graphql
mutation {
accountCreateEmailToken(
userId: "ID.unique()",
email: "email@example.com",
securityPhrase: true
) {
_id
_createdAt
userId
secret
expire
securityPhrase
}
}
```

{% /multicode %}

By enabling security phrase feature, you will recieve `securityPhrase` 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.
Loading