Docs update

This commit is contained in:
Stan Wohlwend 2024-07-30 22:32:50 -07:00
parent cfe5e011af
commit 0e6588f867
13 changed files with 163 additions and 192 deletions

View File

@ -47,18 +47,19 @@ navigation:
path: ./docs/pages/getting-started/production.mdx
- section: Concepts
contents:
# - page: Client vs. Server
# icon: fa-regular fa-code-branch
- page: OAuth Providers
icon: fa-regular fa-key
- page: The StackApp Object
icon: fa-regular fa-folder-gear
path: ./docs/pages/concepts/stack-app.mdx
- page: Connected OAuth Accounts
icon: fa-regular fa-link
path: ./docs/pages/concepts/oauth.mdx
- page: Teams
icon: fa-regular fa-users
path: ./docs/pages/concepts/teams.mdx
path: ./docs/pages/concepts/orgs-and-teams.mdx
- page: Selecting a Team
icon: fa-regular fa-exchange
path: ./docs/pages/concepts/team-selection.mdx
- page: Permissions
- page: Permissions & RBAC
icon: fa-regular fa-user-lock
path: ./docs/pages/concepts/permissions.mdx
# - page: Local Development
@ -85,6 +86,9 @@ navigation:
path: ./docs/pages/customization/page-examples/sign-up.mdx
- tab: sdk
layout:
- page: SDK Overview
icon: fa-regular fa-globe
path: ./docs/pages/sdk/overview.mdx
- section: Classes
contents:
- page: CurrentUser

View File

@ -1,18 +1,24 @@
---
slug: concepts/oauth
subtitle: Integrating OAuth Providers for API Access
subtitle: Managing third-party OAuth access tokens
---
With Stack, users can connect multiple OAuth accounts, enabling access to provider-specific data and services such as Google Drive, Microsoft Calendar, GitHub repositories, and more.
Stack has good support for working with OAuth and OIDC providers, such as Google, Facebook, Microsoft, and others.
## Connecting with an OAuth Provider Post Sign-in
Beyond using OAuth for signing in, Stack can manage your users' access token so you can invoke APIs on their behalf. For example, you can use this to send emails with Gmail, access repositories on GitHub, or access files on OneDrive.
Users can connect with an OAuth provider during sign-in or after signing in using other methods. For the latter, use the `user.useConnectedAccount('<provider_name>', { or: 'redirect' })` method. If the account is already connected, the method returns a Connection object; otherwise, it redirects the user to the provider's authorization page.
A connected account is simply an external account that is linked to the user in some way. If you are not using shared keys (see note below), any user created with "Sign up with OAuth" is automatically connected to the account they signed up with, but it's also possible to connect a user with a provider that is unavailable for sign in.
<Note>
In order to connect accounts after sign-in, you need to set up your own OAuth client ID and client secret with the provider. See more details in the section [Going to Production](../getting-started/production#oauth-providers)
You cannot connect a user's accounts with shared OAuth keys. You need to set up your own OAuth client ID and client secret in Stack's dashboard. For more details, check [Going to Production](../getting-started/production#oauth-providers).
</Note>
## Connecting with OAuth providers
You can access a user's connected account with the `user.getConnectedAccount(providerId)` function or `user.useConnectedAccount(providerId)` hook.
Often, you'll want to redirect the user to the OAuth provider's authorization page if they have not connected the account yet. Just like the `getUser(...)` function, `getConnectedAccount(...)` can also take an `{ or: "redirect" }` argument to achieve this.
Here's how to connect with Google:
```jsx
@ -29,9 +35,10 @@ export default function Page() {
}
```
## Connecting with Extra Scopes
You can request extra scopes when connecting with an OAuth provider. For instance, to access Google Drive, pass the `https://www.googleapis.com/auth/drive.readonly` scope. Here's an example:
## Providing scopes
Most providers have access control in the form of OAuth scopes. These are the permissions that the user will see on the authorization screen (eg. "Your App wants access to your calendar"). For instance, to read Google Drive content, you need the `https://www.googleapis.com/auth/drive.readonly` scope:
```jsx
'use client';
@ -47,9 +54,11 @@ export default function Page() {
}
```
## Retrieving the Access Token
Check your provider's API documentation to find a list of available scopes.
Once connected with an OAuth provider, obtain the access token using the `account.getAccessToken()` method. Use this token to access the provider's API endpoints. Here's an example of using the access token to interact with Google Drive API:
## Retrieving the access token
Once connected with an OAuth provider, obtain the access token with the `account.getAccessToken()` function. Check your provider's API documentation to understand how you can use this token to authorize the user in requests.
```jsx
'use client';
@ -60,37 +69,33 @@ import { useUser } from "@stackframe/stack";
export default function Page() {
const user = useUser({ or: 'redirect' });
const account = user.useConnectedAccount('google', { or: 'redirect', scopes: ['https://www.googleapis.com/auth/drive.readonly'] });
const tokens = account.useAccessToken();
const { accessToken } = account.useAccessToken();
const [response, setResponse] = useState<any>();
useEffect(() => {
fetch('https://www.googleapis.com/drive/v3/files', {
headers: { Authorization: `Bearer ${tokens.accessToken}` }
headers: { Authorization: `Bearer ${accessToken}` }
})
.then((res) => res.json())
.then((data) => setResponse(data))
.catch((err) => console.error(err));
}, [tokens]);
}, [accessToken]);
return <div>{response ? JSON.stringify(response) : 'Loading...'}</div>;
}
```
## Requesting Extra Scopes During Sign-in
## Sign-in default scopes
To avoid showing the authorization page twice, you can request extra scopes during the sign-in process. This approach is optional and depends on your application's design. Some applications may prefer to request extra permissions only when needed, while others might want to obtain all necessary permissions upfront.
To avoid showing the authorization page twice, you can already request scopes during the sign-in flow. This approach is optional. Some applications may prefer to request extra permissions only when needed, while others might want to obtain all necessary permissions upfront.
Configure the `StackServerApp` with the required scopes:
To do this, edit the `oauthScopesOnSignIn` setting of your `stackServerApp`:
```jsx title='stack.ts'
// imports ...
export const stackServerApp = new StackServerApp({
// your other settings ...
// ...your other settings...
oauthScopesOnSignIn: {
google: ['https://www.googleapis.com/authdrive.readonly']
}
});
```
By setting this up, users will be prompted for all necessary permissions during the initial sign-in, avoiding the need for them to approve additional permissions later. Choose the approach that best fits your application's user experience and workflow.

View File

@ -0,0 +1,78 @@
---
slug: concepts/orgs-and-teams
subtitle: Manage teams and team members
---
Teams are a structured way to group users and manage their permissions. Users can belong to multiple teams at the same time, so they can represent departments, B2B customers, or projects.
## Creating a team
To create a team and add/remove users, use the `createTeam` function on `stackServerApp`:
```tsx
const team = await stackServerApp.createTeam({
displayName: 'New Team',
});
await team.update({
displayName: 'New Team Name',
});
// you can retrieve the team by its ID:
const sameTeam = await stackServerApp.getTeam(team.id);
// or list all teams that exist on the entire app:
const allTeams = await stackServerApp.listTeams();
```
## Managing users
You can add, remove and list users on a [`ServerTeam`](/concepts/stack-app#client-vs-server):
```tsx
const allUsers = await team.listUsers();
await team.addUser(user);
await team.removeUser(user);
```
## Retrieving a user's teams
You can list all teams that a user is a member of with the `listTeams` or `useTeams` functions, or get a specific one with `getTeam` or `useTeam`. This works on clients and servers.
<Tabs>
<Tab title="Client Component">
```tsx
const user = useUser({ or: 'redirect' });
const allTeams = user.useTeams();
const someTeam = user.useTeam('some-team-id'); // may be null if the user is not a member of this team
return (
<div>
{allTeams.map(team => (
<div key={team.id}>{team.displayName}</div>
))}
</div>
<div>
{someTeam ? team.displayName : 'Not a member of this team'}
</div>
);
```
</Tab>
<Tab title="Server Component">
```tsx
const user = await stackServerApp.getUser({ or: 'redirect' });
const allTeams = await user.listTeams();
const someTeam = await user.getTeam('some-team-id'); // may be null if the user is not a member of this team
return (
<div>
{allTeams.map(team => (
<div key={team.id}>{team.displayName}</div>
))}
</div>
<div>
{someTeam ? team.displayName : 'Not a member of this team'}
</div>
);
```
</Tab>
</Tabs>

View File

@ -0,0 +1,26 @@
---
slug: concepts/stack-app
subtitle: The most important object of your Stack project
---
By now, you may have seen the `useApp()` hook and the `stackServerApp` variable. Both return a `StackApp`, of type `StackClientApp` and `StackServerApp` respectively.
Nearly all of Stack's functionality is on your `StackApp` object. Think of this object as the "connection" from your code to Stack's servers. Each app is always associated with one specific project ID (by default the one found in your environment variables).
There is also a page on [StackApp](../sdk/stack-app) in the SDK reference, which lists all available functions.
## `getXyz`/`listXyz` vs. `useXyz`
You will see that most of the asynchronous functions on `StackApp` come in two flavors: `getXyz`/`listXyz` and `useXyz`. The former are asynchronous fetching functions which return a `Promise`, while the latter are React hooks that [suspend](https://react.dev/reference/react/Suspense) the current component until the data is available.
Normally, you would choose between the two based on whether you are in a React Server Component or a React Client Component. However, there are some scenarios where you use `getXyz` on the client, for example as the callback of an `onClick` handler.
## Client vs. server
`StackClientApp` is the app which contains everything needed to build a frontend application, for example the own user object. It requires a publishable client key in its initialization (usually set by the `NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY` environment variable).
`StackServerApp` has all the functionality of `StackClientApp`, but also some server-only functions, eg. listing or modifying ALL users. This requires a secret server key (usually set by the `STACK_SECRET_SERVER_KEY` environment variable), which **must always be kept secret**.
There is also a third type, `StackAdminApp`, but it is rarely used. It is meant for internal tools that have special requirements, and can edit the Stack project configuration. This is useful for configuring Stack programmatically, for example with Terraform.
Some of the functions have different return types; for example, `StackClientApp.getUser()` returns a `Promise<User>` while `StackServerApp.getUser()` returns a `Promise<ServerUser>`. The `Server` or `Admin` prefixes indicates that the object contains server-/admin-only functionality respectively.

View File

@ -1,153 +0,0 @@
---
slug: getting-started/teams
subtitle: Manage teams and team members
---
Teams provide a structured way to group users and manage their permissions. Users can belong to multiple teams, which can represent departments, B2B customers, or projects.
To assign users to a default team upon sign-up, activate the corresponding toggle in the Stack dashboard under the team settings tab. This setting automatically assigns each new user to a team.
## Creating a Team
To create a team, use the `createTeam` method on the `stackServerApp`. Here's an example:
```tsx title="Create a Team"
const team = await stackServerApp.createTeam({
displayName: 'New Team',
});
```
## Adding a User to a Team
To add a user to a team, use the `addUser` method on the `Team` object. Here's how:
```tsx title="Add a User to a Team"
const team = await stackServerApp.getTeam('teamId');
await team.addUser(user);
```
## List All the Teams of a User
You can list all the teams a user belongs to by using the `listTeams` method or the `useTeams` hook on the User object. Here's how to do it:
<Tabs>
<Tab title="Client Component">
```tsx title="List teams of a user on the client"
"use client";
import { useUser } from "@stackframe/stack";
export function DisplayUserTeams() {
const user = useUser({ or: 'redirect' });
const teams = user.useTeams();
return (
<div>
{teams.map(team => (
<div key={team.id}>{team.displayName}</div>
))}
</div>
);
}
```
</Tab>
<Tab title="Server Component">
```tsx title="List teams of a user on the server"
import { stackServerApp } from "@/stack";
export default async function DisplayUserTeams() {
const user = await stackServerApp.getUser({ or: 'redirect' });
const teams = await user.listTeams();
return (
<div>
{teams.map(team => (
<div key={team.id}>{team.displayName}</div>
))}
</div>
);
}
```
</Tab>
</Tabs>
## Get Specific Team of a User
To obtain details of a specific team a user belongs to, use the `getTeam` method or `useTeam` hook. Note: this might return `null` if the user is not a member of that team. Here's an example:
<Tabs>
<Tab title="Client Component">
```tsx title="Get a specific team of a user on the client"
"use client";
import { useUser } from "@stackframe/stack";
export function DisplayUserTeam(props: { teamId: string }) {
const user = useUser({ or: 'redirect' });
const team = user.useTeam(props.teamId);
return (
<div>
{team ? team.displayName : 'Not a member of this team'}
</div>
);
}
```
</Tab>
<Tab title="Server Component">
```tsx title="Get a specific team of a user on the server"
import { stackServerApp } from "@/stack";
export default async function DisplayUserTeam(props: { teamId: string }) {
const user = await stackServerApp.getUser({ or: 'redirect' });
const team = await user.getTeam(props.teamId);
return (
<div>
{team ? team.displayName : 'Not a member of this team'}
</div>
);
}
```
</Tab>
</Tabs>
## List All the Teams
To list all teams, use the `listTeams` method on the `stackServerApp`. Here's an example:
```tsx
const teams = await stackServerApp.listTeams();
```
## Update a Team
To update a team, use the `update` method on a server-side team object. Here's how to do it:
```tsx
const team = await stackServerApp.getTeam('teamId');
await team.update({
displayName: 'New Team Name',
});
```
## Remove a User from a Team
To remove a user from a team, use the `removeUser` method on the `team` object. Here's an example:
```tsx
const team = await stackServerApp.getTeam(teamId);
await team?.removeUser(userId);
```
## List All the Users of a Team
To list all the users of a team, use the `listUsers` method on a server-side team object. Here's an example:
```tsx
const team = await stackServerApp.getTeam('teamId');
const users = await team.listUsers();
for (const user of users) {
console.log(`${user.displayName} is a member of ${team.displayName}`);
}
```

View File

@ -37,7 +37,7 @@ export const stackServerApp = new StackServerApp({
You are now all set! If you visit the `/signin` page, you should see your custom sign in page. If your user visits a protected page or the old `/handler/sign-in` URL, they will be redirected to your new sign-in page.
For more examples, please refer to the [Examples](/docs/category/page-examples).
For more examples, please refer to the [Examples](/category/page-examples).
## From scratch

View File

@ -28,7 +28,7 @@ You can also use `useUser` at the beginning of the sign in page to check if weth
## Custom OAuth Sign Up
OAuth sign-in and sign-up shares the same function. Check out the [Sign In example](/docs/customization/page-examples/signin#custom-oauth-sign-in) for more information.
OAuth sign-in and sign-up shares the same function. Check out the [Sign In example](/customization/page-examples/signin#custom-oauth-sign-in) for more information.
## Custom Credential Sign Up
@ -70,4 +70,4 @@ export default function CustomCredentialSignUp() {
## Custom Magic Link Sign Up
Magic link sign-in and sign-up shares the same function. Check out the [Sign In example](/docs/customization/page-examples/signin#custom-magic-link-sign-in) for more information.
Magic link sign-in and sign-up shares the same function. Check out the [Sign In example](/customization/page-examples/signin#custom-magic-link-sign-in) for more information.

View File

@ -6,13 +6,13 @@ subtitle: Frequently asked questions about Stack
## Languages & Frameworks
<AccordionGroup>
<Accordion title="What languages are supported?">
In the frontend, Stack supports TypeScript and JavaScript with Next.js. In the backend, Stack has a flexible [REST API](/docs/rest-api) that can be used with any language or framework.
In the frontend, Stack supports TypeScript and JavaScript with Next.js. In the backend, Stack has a flexible [REST API](/rest-api) that can be used with any language or framework.
</Accordion>
<Accordion title="Can I use Stack with other JavaScript frameworks, like Astro or Angular?">
While you can use any backend framework with Stack, the frontend is tightly integrated with Next.js. If you want to use a different frontend framework, you will have to build the integration ourselves with the client endpoints of our [REST API](/docs/rest-api). Some members of our community have started projects to do this, so you may want to join [our Discord](https://discord.stack-auth.com) to coordinate with them.
While you can use any backend framework with Stack, the frontend is tightly integrated with Next.js. If you want to use a different frontend framework, you will have to build the integration ourselves with the client endpoints of our [REST API](/rest-api). Some members of our community have started projects to do this, so you may want to join [our Discord](https://discord.stack-auth.com) to coordinate with them.
</Accordion>
<Accordion title="Can I use Stack with the Next.js pages router?">
Only the Next.js app router is currently supported. However, just like any other unsupported framework, you can use the client endpoints of our [REST API](/docs/rest-api) to build your own integration.
Only the Next.js app router is currently supported. However, just like any other unsupported framework, you can use the client endpoints of our [REST API](/rest-api) to build your own integration.
</Accordion>
</AccordionGroup>

View File

@ -5,7 +5,7 @@ subtitle: Pre-built Next.js components to make your life easier
In [the last guide](./setup.mdx), we initialized Stack. This time, we will take a quick look at some of the most useful Next.js components.
For the full documentation of all available components, please refer to the [SDK reference](/docs/sdk).
For the full documentation of all available components, please refer to the [SDK reference](/sdk).
## `<UserButton />`
@ -49,11 +49,11 @@ All of Stack's components are modular and built from smaller primitives. For exa
You can use these components individually to build a custom sign-in component.
To change the default sign-in URL to your own, see the documentation on [custom pages](/docs/customization/custom-pages).
To change the default sign-in URL to your own, see the documentation on [custom pages](/customization/custom-pages).
## Others
Stack has many more components available. For a comprehensive list, please check the [SDK reference](/docs/sdk).
Stack has many more components available. For a comprehensive list, please check the [SDK reference](/sdk).
## Next steps

View File

@ -31,7 +31,7 @@ We recommend using our **setup wizard**, which will automatically detect your pr
### Done!
That's it! The following files should have been created or updated in your project:
- `app/handler/[...stack]/page.tsx`: This file contains the default pages for sign-in, sign-out, account settings, and more. If you prefer, later you will learn how to [use custom pages](/docs/customization/custom-pages) instead.
- `app/handler/[...stack]/page.tsx`: This file contains the default pages for sign-in, sign-out, account settings, and more. If you prefer, later you will learn how to [use custom pages](/customization/custom-pages) instead.
- `app/layout.tsx`: The layout file was updated to wrap the entire body with `StackProvider` and `StackTheme`.
- `app/loading.tsx`: If not yet found, Stack automatically adds a Suspense boundary to your app. This is shown to the user while Stack's async hooks, like `useUser`, are loading.
- `stack.ts`: This file contains the `stackServerApp` which you can use to access Stack from Server Components, Server Actions, API routes, and middleware.
@ -91,7 +91,7 @@ We recommend using our **setup wizard**, which will automatically detect your pr
}
```
This will create pages for sign-in, sign-up, password reset, and others. Additionally, it will be used as a callback URL for OAuth. You can [replace them with your own pages](/docs/customization/custom-pages) later.
This will create pages for sign-in, sign-up, password reset, and others. Additionally, it will be used as a callback URL for OAuth. You can [replace them with your own pages](/customization/custom-pages) later.
### Add StackProvider to `layout.tsx`

View File

@ -3,7 +3,7 @@ slug: getting-started/users
subtitle: Reading and writing user information, and protecting pages
---
In [the last guide](./setup.mdx), we initialized Stack. This created new files containing a `StackServerApp` and `StackProvider`. In this section, we will show you how to utilize those for accessing and modifying the current user information on Server Components and Client Components, respectively.
You will inevitably build custom components that access the user in one way or another. In this section, we will take a closer look at the functions and hooks that let you do this.
## Client Component basics
@ -166,7 +166,7 @@ console.log(user.serverMetadata);
## Signing out
You can sign out the user by redirecting them to `/handler/sign-out` or simply by calling `user.signOut()`. They will be redirected to the URL [configured as `afterSignOut` in the `StackServerApp`](/docs/api-documentation/app).
You can sign out the user by redirecting them to `/handler/sign-out` or simply by calling `user.signOut()`. They will be redirected to the URL [configured as `afterSignOut` in the `StackServerApp`](/sdk/stack-app).
<Tabs>
<Tab title="user.signOut()">

View File

@ -38,8 +38,12 @@ To see how to use these headers in various programming languages, see the [examp
Client access type is mostly used for client-side applications, like a browser or mobile app. The client APIs can only read and update the currently authenticated user's data, and it is usually fine to post the publishable client key in the client-side code.
Server access type, on the other hand, is for your backend server that you control. It has full access over all user data, and the secret server key should never be exposed to client-side code.
For more information, see the concept documentation on [StackApp](../concepts/stack-app#client-vs-server).
</Accordion>
<Accordion title="What is this 'admin' access type that I see?">
If you'd like to build your own version of the Stack dashboard (or update project configuration programmatically), you can use the `admin` access type. These endpoints are very dangerous and you should only use them if you know what you're doing.
For more information, see the concept documentation on [StackApp](../concepts/stack-app#client-vs-server).
</Accordion>
</AccordionGroup>

View File

@ -0,0 +1,7 @@
---
slug: sdk/user
---
Stack has an SDK for Next.js front- and backends. It includes components, hooks, functions, and types to make building your app easier.
**NOTE**: Stack's SDK reference is currently a work in progress. Please check the [Documentation](/) or [API Reference](/rest-api) instead.