Skip to content

Commit

Permalink
Merge branch 'main' into stefanos/eco-257-add-a-docs-page-for-enablin…
Browse files Browse the repository at this point in the history
…g-offline-support
  • Loading branch information
alexisintech committed Dec 10, 2024
2 parents 94817c2 + 06da5f1 commit 2a72633
Show file tree
Hide file tree
Showing 68 changed files with 1,035 additions and 1,010 deletions.
1 change: 1 addition & 0 deletions docs/_partials/clerk-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The [`<ClerkProvider>`](/docs/components/clerk-provider) component provides session and user context to Clerk's hooks and components. It's recommended to wrap your entire app at the entry point with `<ClerkProvider>` to make authentication globally accessible. See the [reference docs](/docs/components/clerk-provider) for other configuration options.
58 changes: 58 additions & 0 deletions docs/_partials/expo/oauth-custom-flow.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
The following example demonstrates how to create a custom OAuth sign-in flow for [Google accounts](/docs/authentication/social-connections/google).

```tsx {{ filename: 'app/(auth)/sign-in.tsx', collapsible: true }}
import React from 'react'
import * as WebBrowser from 'expo-web-browser'
import { Text, View, Button } from 'react-native'
import { Link } from 'expo-router'
import { useOAuth } from '@clerk/clerk-expo'
import * as Linking from 'expo-linking'

export const useWarmUpBrowser = () => {
React.useEffect(() => {
// Warm up the android browser to improve UX
// https://docs.expo.dev/guides/authentication/#improving-user-experience
void WebBrowser.warmUpAsync()
return () => {
void WebBrowser.coolDownAsync()
}
}, [])
}

WebBrowser.maybeCompleteAuthSession()

export default function Page() {
useWarmUpBrowser()

const { startOAuthFlow } = useOAuth({ strategy: 'oauth_google' })

const onPress = React.useCallback(async () => {
try {
const { createdSessionId, signIn, signUp, setActive } = await startOAuthFlow({
redirectUrl: Linking.createURL('/dashboard', { scheme: 'myapp' }),
})

// If sign in was successful, set the active session
if (createdSessionId) {
setActive!({ session: createdSessionId })
} else {
// Use signIn or signUp returned from startOAuthFlow
// for next steps, such as MFA
}
} catch (err) {
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(err, null, 2))
}
}, [])

return (
<View>
<Link href="/">
<Text>Home</Text>
</Link>
<Button title="Sign in with Google" onPress={onPress} />
</View>
)
}
```
6 changes: 3 additions & 3 deletions docs/authentication/enterprise-connections/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ To configure subdomains for a SAML connection:

The following IdPs are supported: Google Workspace and Microsoft Entra ID. For _development instances_, Clerk uses preconfigured shared credentials and redirect URIs—no other configuration is needed. For _production instances_, you must provide custom credentials. Follow the steps outlined in the guides to complete the setup:

- [Google](docs/authentication/social-connections/google)
- [Microsoft](docs/authentication/social-connections/azure)
- [Google](/docs/authentication/social-connections/google)
- [Microsoft](/docs/authentication/social-connections/microsoft)

### Automatic deprovisioning

Clerk prevents users linked to deprovisioned accounts in the OpenID provider from accessing the app.

Within 10 minutes of a user being removed from the OpenID provider (e.g. [suspendeded](https://support.google.com/a/answer/33312?hl=en) or [deleted](https://support.google.com/a/answer/33314?hl=en) via Google Workspace, or [deleted](https://learn.microsoft.com/en-us/entra/fundamentals/how-to-create-delete-users#delete-a-user) via Microsoft Entra), Clerk will recognize that the user has been deprovisioned and will revoke that user's existing sessions.

It is ultimately the app's responsibility to handle this unauthenticated state and display something appropriate to the user. For example, Next.js apps using [`auth.protect()`](docs/references/nextjs/auth#auth-protect) will automatically redirect the user to the sign-in page.
It is ultimately the app's responsibility to handle this unauthenticated state and display something appropriate to the user. For example, Next.js apps using [`auth.protect()`](/docs/references/nextjs/auth#auth-protect) will automatically redirect the user to the sign-in page.

## SAML vs. EASIE

Expand Down
2 changes: 1 addition & 1 deletion docs/backend-requests/making/jwt-templates.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ The result of a conditional expression is that of the first operand that does no

```json
{
"has_verified_contact_info": "{{user.has_verified_email || user.has_verified_phone}}",
"has_verified_contact_info": "{{user.email_verified || user.phone_number_verified}}",

// fallback to a string value
"full_name": "{{user.full_name || 'Awesome User'}}",
Expand Down
4 changes: 2 additions & 2 deletions docs/components/authentication/sign-up.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ All props are optional.
---

- `unsafeMetadata`
- `{[string]: any} | null`
- [`SignUpUnsafeMetadata`](/docs/references/javascript/types/metadata#sign-up-unsafe-metadata)

An object containing key-value pairs for `unsafeMetadata` that will be saved after the user signs up. For example: `{ "company": "companyID1234" }`.
Metadata that can be read and set from the frontend and the backend. Once the sign-up is complete, the value of this field will be automatically copied to the created user's unsafe metadata (`User.unsafeMetadata`). One common use case is to collect custom information about the user during the sign-up process and store it in this property. Read more about [unsafe metadata](/docs/users/metadata#unsafe-metadata).
</Properties>

## Usage with frameworks
Expand Down
8 changes: 4 additions & 4 deletions docs/components/clerk-provider.mdx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: '`<ClerkProvider>`'
description: The <ClerkProvider> component wraps your React application to provide active session and user context to Clerk's hooks and other components.
description: The <ClerkProvider> component provides session and user context to Clerk's hooks and components.
---

The `<ClerkProvider>` component wraps your React application to provide active session and user context to Clerk's hooks and other components.
The `<ClerkProvider>` component is required to integrate Clerk into your React application, providing session and user context to Clerk's hooks and components.

## Usage
The recommended approach is to wrap your entire app with `<ClerkProvider>` at the entry point to make authentication globally accessible. If you only need authentication for specific routes or pieces of your application, render `<ClerkProvider>` deeper in the component tree. This allows you to implement Clerk's functionality precisely where required without impacting the rest of your app.

The `<ClerkProvider>` component must be added to your React entrypoint.
## Usage

<Tabs items={["Next.js", "React"]}>
<Tab>
Expand Down
2 changes: 1 addition & 1 deletion docs/components/organization/create-organization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The following example includes a basic implementation of the `<CreateOrganizatio

<Tabs items={["Next.js", "React", "Remix", "Astro"]}>
<Tab>
```jsx {{ filename: '/app/create-organization/[[...create-organization]]/page.tsx' }}
```jsx {{ filename: 'app/create-organization/[[...create-organization]]/page.tsx' }}
import { CreateOrganization } from '@clerk/nextjs'

export default function CreateOrganizationPage() {
Expand Down
2 changes: 1 addition & 1 deletion docs/components/organization/organization-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ All props are optional.

<Tabs items={["Next.js", "React", "Remix", "Astro"]}>
<Tab>
```jsx {{ filename: '/app/discover/page.tsx' }}
```jsx {{ filename: 'app/discover/page.tsx' }}
import { OrganizationList } from '@clerk/nextjs'

export default function OrganizationListPage() {
Expand Down
2 changes: 1 addition & 1 deletion docs/components/organization/organization-profile.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ All props are optional.
<Tab>
You can embed the `<OrganizationProfile />` component using the [Next.js optional catch-all route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-routes). This allows you to redirect the user inside your application.

```jsx {{ filename: '/app/organization-profile/[[...organization-profile]]/page.tsx' }}
```jsx {{ filename: 'app/organization-profile/[[...organization-profile]]/page.tsx' }}
import { OrganizationProfile } from '@clerk/nextjs'

export default function OrganizationProfilePage() {
Expand Down
2 changes: 1 addition & 1 deletion docs/components/organization/organization-switcher.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ All props below are optional.

<Tabs items={["Next.js", "React", "Remix", "Astro"]}>
<Tab>
```jsx {{ filename: '/app/organization-switcher/[[...organization-switcher]]/page.tsx' }}
```jsx {{ filename: 'app/organization-switcher/[[...organization-switcher]]/page.tsx' }}
import { OrganizationSwitcher } from '@clerk/nextjs'

export default function OrganizationSwitcherPage() {
Expand Down
2 changes: 1 addition & 1 deletion docs/components/protect.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ The following example uses `<Protect>`'s `condition` prop to conditionally rende

<Tabs items={["Next.js", "Astro"]}>
<Tab>
```tsx {{ filename: '/app/dashboard/settings/layout.tsx' }}
```tsx {{ filename: 'app/dashboard/settings/layout.tsx' }}
import type { PropsWithChildren } from 'react'
import { Protect } from '@clerk/nextjs'

Expand Down
2 changes: 1 addition & 1 deletion docs/components/user/user-profile.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ All props are optional.
You can embed the `<UserProfile />` component using the [Next.js optional catch-all route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-routes). This allows you to redirect the user inside your application.

<CodeBlockTabs options={["App Router", "Pages Router"]}>
```jsx {{ filename: '/app/user-profile/[[...user-profile]]/page.tsx' }}
```jsx {{ filename: 'app/user-profile/[[...user-profile]]/page.tsx' }}
import { UserProfile } from '@clerk/nextjs'

const UserProfilePage = () => <UserProfile path="/user-profile" />
Expand Down
2 changes: 1 addition & 1 deletion docs/components/waitlist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ The following example includes a basic implementation of the `<Waitlist />` comp

<Tabs items={["Next.js", "React"]}>
<Tab>
```jsx {{ filename: '/app/waitlist/[[...waitlist]]/page.tsx' }}
```jsx {{ filename: 'app/waitlist/[[...waitlist]]/page.tsx' }}
import { Waitlist } from '@clerk/nextjs'

export default function WaitlistPage() {
Expand Down
68 changes: 44 additions & 24 deletions docs/custom-flows/email-password-mfa.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This guide will walk you through how to build a custom email/password sign-in fl

1. In the Clerk Dashboard, navigate to the [**Multi-factor**](https://dashboard.clerk.com/last-active?path=user-authentication/multi-factor) page.
1. For the purpose of this guide, toggle on both the **Authenticator application** and **Backup codes** strategies.
1. Select **Save**.

### Sign-in flow

Expand Down Expand Up @@ -309,19 +310,34 @@ This guide will walk you through how to build a custom email/password sign-in fl

### Build the flow

Create a component that handles the sign-in with multi-factor authentication flow.
1. Create the `(auth)` route group. This groups your sign-up and sign-in pages.
1. In the `(auth)` group, create a `_layout.tsx` file with the following code. The [`useAuth()`](/docs/references/react/use-auth) hook is used to access the user's authentication state. If the user's already signed in, they'll be redirected to the home page.

> [!NOTE]
> You can render this component in a custom sign-in or sign-up flow, which you can find examples of in [the Expo quickstart](/docs/quickstarts/expo).
```tsx {{ filename: 'app/(auth)/_layout.tsx' }}
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@clerk/clerk-expo'

```tsx {{ filename: 'SignInMFAForm.tsx', collapsible: true }}
export default function AuthenticatedLayout() {
const { isSignedIn } = useAuth()

if (isSignedIn) {
return <Redirect href={'/'} />
}

return <Stack />
}
```

In the `(auth)` group, create a `sign-in.tsx` file with the following code. The [`useSignIn()`](/docs/references/react/use-sign-in) hook is used to create a sign-in flow. The user can sign in using their email and password and will be prompted to verify their account with a code from their authenticator app or with a backup code.

```tsx {{ filename: 'app/(auth)/sign-in.tsx', collapsible: true }}
import React from 'react'
import { useSignIn } from '@clerk/clerk-expo'
import { useRouter } from 'expo-router'
import { Text, TextInput, TouchableOpacity, View } from 'react-native'
import { Text, TextInput, Button, View } from 'react-native'
import Checkbox from 'expo-checkbox'

export default function SignInMFAForm() {
export default function Page() {
const { signIn, setActive, isLoaded } = useSignIn()

const [email, setEmail] = React.useState('')
Expand All @@ -335,18 +351,24 @@ This guide will walk you through how to build a custom email/password sign-in fl
const handleFirstStage = async () => {
if (!isLoaded) return

// Attempt to sign in using the email and password provided
try {
const attemptFirstFactor = await signIn.create({
identifier: email,
password,
})

// If the sign-in was successful, set the session to active
// and redirect the user
if (attemptFirstFactor.status === 'complete') {
await setActive({ session: attemptFirstFactor.createdSessionId })
router.replace('/dashboard')
router.replace('/')
} else if (attemptFirstFactor.status === 'needs_second_factor') {
// If the sign-in requires a second factor, display the TOTP form
setDisplayTOTP(true)
} else {
// If the sign-in failed, check why. User might need to
// complete further steps.
console.error(JSON.stringify(attemptFirstFactor, null, 2))
}
} catch (err) {
Expand All @@ -356,6 +378,7 @@ This guide will walk you through how to build a custom email/password sign-in fl
}
}

// Handle the submission of the TOTP or backup code
const onPressTOTP = React.useCallback(async () => {
if (!isLoaded) return

Expand All @@ -371,13 +394,13 @@ This guide will walk you through how to build a custom email/password sign-in fl
if (attemptSecondFactor.status === 'complete') {
await setActive({ session: attemptSecondFactor.createdSessionId })

router.replace('/dashboard')
router.replace('/')
} else {
// If the status is not complete, check why. User may need to
// complete further steps.
console.error(JSON.stringify(attemptSecondFactor, null, 2))
}
} catch (err: any) {
} catch (err) {
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(err, null, 2))
Expand All @@ -390,48 +413,45 @@ This guide will walk you through how to build a custom email/password sign-in fl
<Text>Verify your account</Text>

<View>
<Text>Code</Text>
<TextInput
value={code}
placeholder="OTP or Backup Code"
placeholder="Enter the code"
placeholderTextColor="#666666"
onChangeText={(c) => setCode(c)}
/>
</View>
<View>
<Text>This code is a backup code</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
<Text>Check if this code is a backup code</Text>
<Checkbox value={useBackupCode} onValueChange={() => setUseBackupCode((prev) => !prev)} />
</View>
<TouchableOpacity onPress={onPressTOTP}>
<Text>Verify</Text>
</TouchableOpacity>
<Button title="Verify" onPress={onPressTOTP} />
</View>
)
}

return (
<View>
<Text>Sign In</Text>
<Text>Sign in</Text>
<View>
<TextInput
value={email}
placeholder="Email..."
placeholderTextColor="#000"
placeholder="Enter email"
placeholderTextColor="#666666"
onChangeText={(email) => setEmail(email)}
/>
</View>

<View>
<TextInput
value={password}
placeholder="Password..."
placeholderTextColor="#000"
placeholder="Enter password"
placeholderTextColor="#666666"
secureTextEntry={true}
onChangeText={(password) => setPassword(password)}
/>
</View>

<TouchableOpacity onPress={handleFirstStage}>
<Text>Sign In</Text>
</TouchableOpacity>
<Button title="Continue" onPress={handleFirstStage} />
</View>
)
}
Expand Down
Loading

0 comments on commit 2a72633

Please sign in to comment.