Connected OAuth Accounts

Managing third-party OAuth access tokens

Stack has good support for working with OAuth and OIDC providers, such as Google, Facebook, Microsoft, and others.

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.

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.

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.

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:

1'use client';
2
3import { useUser } from "@stackframe/stack";
4
5export default function Page() {
6 const user = useUser({ or: 'redirect' });
7 // Redirects to Google authorization if not already connected
8 const account = user.useConnectedAccount('google', { or: 'redirect' });
9 // Account is always defined because of the redirect
10 return <div>Google account connected</div>;
11}

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:

1'use client';
2
3import { useUser } from "@stackframe/stack";
4
5export default function Page() {
6 const user = useUser({ or: 'redirect' });
7 // Redirects to the Google authorization page, requesting access to Google Drive
8 const account = user.useConnectedAccount('google', { or: 'redirect', scopes: ['https://www.googleapis.com/authdrive.readonly'] });
9 // Account is always defined because of the redirect
10 return <div>Google Drive connected</div>;
11}

Check your provider’s API documentation to find a list of available scopes.

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.

1'use client';
2
3import { useEffect, useState } from 'react';
4import { useUser } from "@stackframe/stack";
5
6export default function Page() {
7 const user = useUser({ or: 'redirect' });
8 const account = user.useConnectedAccount('google', { or: 'redirect', scopes: ['https://www.googleapis.com/auth/drive.readonly'] });
9 const { accessToken } = account.useAccessToken();
10 const [response, setResponse] = useState<any>();
11
12 useEffect(() => {
13 fetch('https://www.googleapis.com/drive/v3/files', {
14 headers: { Authorization: `Bearer ${accessToken}` }
15 })
16 .then((res) => res.json())
17 .then((data) => setResponse(data))
18 .catch((err) => console.error(err));
19 }, [accessToken]);
20
21 return <div>{response ? JSON.stringify(response) : 'Loading...'}</div>;
22}

Sign-in default scopes

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.

To do this, edit the oauthScopesOnSignIn setting of your stackServerApp:

stack.ts
1export const stackServerApp = new StackServerApp({
2 // ...your other settings...
3 oauthScopesOnSignIn: {
4 google: ['https://www.googleapis.com/authdrive.readonly']
5 }
6});