Skip to content

Commit

Permalink
Merge branch 'main' into rob/eco-224-vue-sdk-references
Browse files Browse the repository at this point in the history
  • Loading branch information
victoriaxyz authored Dec 16, 2024
2 parents beea2ce + 4b13ae7 commit aead41f
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 58 deletions.
93 changes: 35 additions & 58 deletions docs/custom-flows/error-handling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ description: Provide your users with useful information about the errors being r

Clerk-related errors are returned as an array of [`ClerkAPIError`](/docs/references/javascript/types/clerk-api-error) objects. These errors contain a `code`, `message`, `longMessage` and `meta` property. These properties can be used to provide your users with useful information about the errors being returned from sign-up and sign-in requests.

This guide demonstrates how to handle Clerk-related errors when building custom flows.

> [!TIP]
> To see a list of all possible errors, refer to the [Errors](/docs/errors/overview) documentation.
Expand All @@ -18,7 +16,7 @@ The following example uses the [email & password sign-in custom flow](/docs/cust
<Tab>
This example is written for Next.js App Router but it can be adapted for any React meta framework, such as Remix.

```tsx {{ filename: 'app/sign-in/[[...sign-in]]/page.tsx' }}
```tsx {{ filename: 'app/sign-in/[[...sign-in]]/page.tsx', mark: [[6, 7], 13, [21, 22], [45, 48], [79, 85]] }}
'use client'

import * as React from 'react'
Expand All @@ -39,7 +37,7 @@ The following example uses the [email & password sign-in custom flow](/docs/cust
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()

// clear any errors that may have occured during previous form submission
// Clear any errors that may have occurred during previous form submission
setErrors(undefined)

if (!isLoaded) {
Expand All @@ -48,21 +46,20 @@ The following example uses the [email & password sign-in custom flow](/docs/cust

// Start the sign-in process using the email and password provided
try {
const completeSignIn = await signIn.create({
const signInAttempt = await signIn.create({
identifier: email,
password,
})

// This is mainly for debugging while developing.
if (completeSignIn.status !== 'complete') {
console.log(JSON.stringify(completeSignIn, null, 2))
}

// If sign-in process is complete, set the created session as active
// and redirect the user
if (completeSignIn.status === 'complete') {
await setActive({ session: completeSignIn.createdSessionId })
if (signInAttempt.status === 'complete') {
await setActive({ session: signInAttempt.createdSessionId })
router.push('/')
} else {
// If the status is not complete, check why. User may need to
// complete further steps.
console.error(JSON.stringify(signInAttempt, null, 2))
}
} catch (err) {
if (isClerkAPIResponseError(err)) setErrors(err.errors)
Expand Down Expand Up @@ -113,7 +110,7 @@ The following example uses the [email & password sign-in custom flow](/docs/cust

<Tab>
<CodeBlockTabs options={["index.html", "main.js"]}>
```html {{ filename: 'index.html' }}
```html {{ filename: 'index.html', mark: [22] }}
<!doctype html>
<html lang="en">
<head>
Expand All @@ -124,32 +121,25 @@ The following example uses the [email & password sign-in custom flow](/docs/cust
<body>
<div id="signed-in"></div>

<div id="sign-up">
<h2>Sign up</h2>
<form id="sign-up-form">
<div id="sign-in">
<h2>Sign in</h2>
<form id="sign-in-form">
<label for="email">Enter email address</label>
<input type="email" name="email" id="sign-up-email" />
<input name="email" id="sign-in-email" />
<label for="password">Enter password</label>
<input type="password" name="password" id="sign-up-password" />
<input name="password" id="sign-in-password" />
<button type="submit">Continue</button>
</form>
</div>

<form id="verifying" hidden>
<h2>Verify your email</h2>
<label for="code">Enter your verification code</label>
<input id="code" name="code" />
<button type="submit" id="verify-button">Verify</button>
</form>

<p id="error"></p>

<script type="module" src="/src/main.js" async crossorigin="anonymous"></script>
</body>
</html>
```

```js {{ filename: 'main.js' }}
```js {{ filename: 'main.js', mark: [[43, 49]] }}
import { Clerk } from '@clerk/clerk-js'

const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
Expand All @@ -160,52 +150,39 @@ The following example uses the [email & password sign-in custom flow](/docs/cust
if (clerk.user) {
// Mount user button component
document.getElementById('signed-in').innerHTML = `
<div id="user-button"></div>
`
<div id="user-button"></div>
`

const userbuttonDiv = document.getElementById('user-button')

clerk.mountUserButton(userbuttonDiv)
} else {
// Handle the sign-up form
document.getElementById('sign-up-form').addEventListener('submit', async (e) => {
// Handle the sign-in form
document.getElementById('sign-in-form').addEventListener('submit', async (e) => {
e.preventDefault()

const formData = new FormData(e.target)
const emailAddress = formData.get('email')
const password = formData.get('password')

try {
// Start the sign-up process using the phone number method
await clerk.client.signUp.create({ emailAddress, password })
await clerk.client.signUp.prepareEmailAddressVerification()
// Hide sign-up form
document.getElementById('sign-up').setAttribute('hidden', '')
// Show verification form
document.getElementById('verifying').removeAttribute('hidden')
} catch (err) {
if (isClerkAPIResponseError(err)) {
const errors = err.errors
document.getElementById('error').textContent = errors[0].longMessage
}
console.error(JSON.stringify(err, null, 2))
}
})

// Handle the verification form
document.getElementById('verifying').addEventListener('submit', async (e) => {
const formData = new FormData(e.target)
const code = formData.get('code')

try {
// Verify the phone number
const verify = await clerk.client.signUp.attemptEmailAddressVerification({
code,
// Start the sign-in process
const signInAttempt = await clerk.client.signIn.create({
identifier: emailAddress,
password,
})

// Now that the user is created, set the session to active.
await clerk.setActive({ session: verify.createdSessionId })
} catch (err) {
// If the sign-in is complete, set the user as active
if (signInAttempt.status === 'complete') {
await clerk.setActive({ session: signInAttempt.createdSessionId })

location.reload()
} else {
// If the status is not complete, check why. User may need to
// complete further steps.
console.error(JSON.stringify(signInAttempt, null, 2))
}
} catch (error) {
if (isClerkAPIResponseError(err)) {
const errors = err.errors
document.getElementById('error').textContent = errors[0].longMessage
Expand Down
6 changes: 6 additions & 0 deletions docs/how-clerk-works/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ A user's process of signing in would work as follows. This example assumes that
muted
loop
playsInline
controls
/>
1. The next time the browser sends a request to the server, it [automatically includes](/docs/how-clerk-works/cookies) the session cookie. The server checks the database for the session ID and retrieves the associated user ID and session metadata. If the session is valid and active, the server has verified that the user has authenticated, and can then use the user ID to fetch any required user data and process the request.
<Video
Expand All @@ -90,6 +91,7 @@ A user's process of signing in would work as follows. This example assumes that
muted
loop
playsInline
controls
/>

> [!NOTE]
Expand All @@ -114,6 +116,7 @@ The stateless authentication flow operates as follows. This example assumes that
muted
loop
playsInline
controls
/>
1. The next time the browser sends a request to the server, it sends the cookie containing the token. The server verifies the signature of the token to ensure that it's valid, and then uses the user ID within the token to fetch any required user data and process the request.
<Video
Expand All @@ -124,6 +127,7 @@ The stateless authentication flow operates as follows. This example assumes that
muted
loop
playsInline
controls
/>

While more complex to implement, this stateless model offers significant advantages. Because verifying the JWT doesn't require interacting with a database, the latency overhead and scaling challenges caused by database lookups are eliminated, leading to faster request processing.
Expand Down Expand Up @@ -184,6 +188,7 @@ This example assumes that the user already signed up and their credentials are s
muted
loop
playsInline
controls
/>
1. And just like stateless auth, the next time the browser sends a request to the server, it sends the cookie containing the token. The server verifies the signature of the token to ensure that it's valid, and then uses the user ID within the token to fetch any required user data and process the request.
<Video
Expand All @@ -194,6 +199,7 @@ This example assumes that the user already signed up and their credentials are s
muted
loop
playsInline
controls
/>

So far, this is the same as stateless auth, with one key distinction: the session token's expiration time. This is because normally, in stateless authentication implementations, the token's expiration is set to match the intended session duration - commonly ranging from one week to one month. But since JWTs can't be revoked, if a token is compromised, the attacker has the entirety of the session lifetime to be able to take over the user's account. This will be several days at least on average, if not several weeks.
Expand Down
28 changes: 28 additions & 0 deletions docs/references/chrome-extension/sync-host.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ Clerk allows you to sync the authentication state from your web app to your Chro
}
```

### Hide unsupported authentication methods

When using the Sync Host feature, authentication methods that you want to use in your web app [may not be fully supported in the Chrome Extension environment](/docs/references/chrome-extension/overview#authentication-options). To hide unsupported methods in your Chrome Extension, you can use the [`appearance`](https://clerk.com/docs/customization/overview) prop with your extension's `<SignUp>` and `<SignIn>` components as demonstrated in the following examples.

<CodeBlockTabs options={["<SignUp>", "<SignIn>"]}>
```tsx {{ filename: 'src/popup/pages/sign-up.tsx', mark: [[3, 7]] }}
<SignUp
appearance={{
elements: {
socialButtonsRoot: 'plasmo-hidden',
dividerRow: 'plasmo-hidden',
},
}}
/>
```

```tsx {{ filename: 'src/popup/pages/sign-in.tsx', mark: [[3, 7]] }}
<SignIn
appearance={{
elements: {
socialButtonsRoot: 'plasmo-hidden',
dividerRow: 'plasmo-hidden',
},
}}
/>
```
</CodeBlockTabs>

## Configure `host_permissions`

`host_permissions` specifies which hosts, or websites, will have permission to sync auth state with your app. It accepts an array, allowing you to add more than one host.
Expand Down

0 comments on commit aead41f

Please sign in to comment.