Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions apps/public/content/docs/self-hosting/environment-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,112 @@ If `SMTP_HOST` is set and an SMTP send attempt fails, the system will **not** au

## OAuth & Integrations

Social login (GitHub / Google) is **off by default** for self-hosted installs. Set the provider credentials below to show the matching button on `/login` and `/onboarding`. Leave them unset (or set `DISABLE_*_AUTH`) to hide a provider.

### GITHUB_CLIENT_ID

**Type**: `string`
**Required**: No (required to enable GitHub login)
**Default**: None

OAuth App client ID from [GitHub Developer Settings](https://github.com/settings/developers).

**Example**:
```bash
GITHUB_CLIENT_ID=Ov23liABCDEFG
```

### GITHUB_CLIENT_SECRET

**Type**: `string`
**Required**: No (required to enable GitHub login)
**Default**: None

OAuth App client secret from GitHub.

**Example**:
```bash
GITHUB_CLIENT_SECRET=your-github-client-secret
```

### GITHUB_REDIRECT_URI

**Type**: `string`
**Required**: No (required to enable GitHub login)
**Default**: None

Must match the **Authorization callback URL** configured on the GitHub OAuth App. Point it at your API host:

**Example**:
```bash
GITHUB_REDIRECT_URI=https://api.example.com/oauth/github/callback
```

### DISABLE_GITHUB_AUTH

**Type**: `boolean`
**Required**: No
**Default**: `false`

Set to `true` or `1` to hide GitHub login even when GitHub credentials are set.

**Example**:
```bash
DISABLE_GITHUB_AUTH=true
```

### GOOGLE_CLIENT_ID

**Type**: `string`
**Required**: No (required to enable Google login)
**Default**: None

OAuth 2.0 Client ID from [Google Cloud Console](https://console.cloud.google.com/apis/credentials). The same client can also be used for Google Search Console integration.

**Example**:
```bash
GOOGLE_CLIENT_ID=1234567890-abcdefg.apps.googleusercontent.com
```

### GOOGLE_CLIENT_SECRET

**Type**: `string`
**Required**: No (required to enable Google login)
**Default**: None

OAuth 2.0 Client secret from Google Cloud Console.

**Example**:
```bash
GOOGLE_CLIENT_SECRET=your-google-client-secret
```

### GOOGLE_REDIRECT_URI

**Type**: `string`
**Required**: No (required to enable Google login)
**Default**: None

Must match an authorized redirect URI on the Google OAuth client. Point it at your API host:

**Example**:
```bash
GOOGLE_REDIRECT_URI=https://api.example.com/oauth/google/callback
```

### DISABLE_GOOGLE_AUTH

**Type**: `boolean`
**Required**: No
**Default**: `false`

Set to `true` or `1` to hide Google login while keeping `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` available for Search Console (`GSC_GOOGLE_REDIRECT_URI`).

**Example**:
```bash
DISABLE_GOOGLE_AUTH=true
```

### SLACK_CLIENT_ID

**Type**: `string`
Expand Down
43 changes: 43 additions & 0 deletions apps/public/content/docs/self-hosting/self-hosting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,49 @@ Invitations are enabled by default. You can also disable invitations by setting
ALLOW_INVITATION=false
```

### Social login (GitHub / Google)

Email/password works without extra setup. GitHub and Google buttons appear only when their OAuth credentials are configured.

#### GitHub

1. Open [GitHub → Settings → Developer settings → OAuth Apps](https://github.com/settings/developers) and create a new OAuth App.
2. Set **Homepage URL** to your dashboard URL (e.g. `https://analytics.example.com`).
3. Set **Authorization callback URL** to your API callback:

```text
https://api.example.com/oauth/github/callback
```

4. Copy the Client ID and generate a Client Secret, then set:

```bash title=".env"
GITHUB_CLIENT_ID=…
GITHUB_CLIENT_SECRET=…
GITHUB_REDIRECT_URI=https://api.example.com/oauth/github/callback
```

To hide GitHub login while keeping credentials around, set `DISABLE_GITHUB_AUTH=true`.

#### Google

1. In [Google Cloud Console → Credentials](https://console.cloud.google.com/apis/credentials), create an OAuth 2.0 Client ID (Web application).
2. Add an authorized redirect URI:

```text
https://api.example.com/oauth/google/callback
```

3. Set:

```bash title=".env"
GOOGLE_CLIENT_ID=…
GOOGLE_CLIENT_SECRET=…
GOOGLE_REDIRECT_URI=https://api.example.com/oauth/google/callback
```

If you use the same Google client for Search Console but do not want Google on the login page, set `DISABLE_GOOGLE_AUTH=true`.

For a complete reference of all environment variables, see the [Environment Variables documentation](/docs/self-hosting/environment-variables).

## Helpful scripts
Expand Down
18 changes: 18 additions & 0 deletions apps/start/src/hooks/use-oauth-providers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from '@/integrations/trpc/react';

const DISABLED = { github: false, google: false } as const;

/** Which social login buttons the server currently offers. */
export function useOAuthProviders() {
const trpc = useTRPC();
const query = useQuery(trpc.auth.getOAuthProviders.queryOptions());

return {
...query,
providers: query.data ?? DISABLED,
hasAny:
query.data !== undefined &&
(query.data.github || query.data.google),
};
}
41 changes: 27 additions & 14 deletions apps/start/src/routes/_login.login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SignInGithub } from '@/components/auth/sign-in-github';
import { SignInGoogle } from '@/components/auth/sign-in-google';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { useCookieStore } from '@/hooks/use-cookie-store';
import { useOAuthProviders } from '@/hooks/use-oauth-providers';
import { createTitle, PAGE_TITLES } from '@/utils/title';

export const Route = createFileRoute('/_login/login')({
Expand All @@ -22,14 +23,20 @@ export const Route = createFileRoute('/_login/login')({
correlationId: z.string().optional(),
inviteId: z.string().optional(),
}),
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(
context.trpc.auth.getOAuthProviders.queryOptions(),
);
},
});

function LoginPage() {
const { error, correlationId, inviteId } = Route.useSearch();
const [lastProvider] = useCookieStore<null | string>(
'last-auth-provider',
null
null,
);
const { providers, hasAny } = useOAuthProviders();

return (
<div className="col w-full gap-8 text-left">
Expand Down Expand Up @@ -72,19 +79,25 @@ function LoginPage() {
</Alert>
)}

<div className="space-y-4">
<SignInGoogle
inviteId={inviteId}
isLastUsed={lastProvider === 'google'}
type="sign-in"
/>
<SignInGithub
inviteId={inviteId}
isLastUsed={lastProvider === 'github'}
type="sign-in"
/>
</div>
<Or />
{hasAny && (
<div className="space-y-4">
{providers.google && (
<SignInGoogle
inviteId={inviteId}
isLastUsed={lastProvider === 'google'}
type="sign-in"
/>
)}
{providers.github && (
<SignInGithub
inviteId={inviteId}
isLastUsed={lastProvider === 'github'}
type="sign-in"
/>
)}
</div>
)}
{hasAny && <Or />}
<SignInEmailForm inviteId={inviteId} isLastUsed={lastProvider === 'email'} />
</div>
);
Expand Down
35 changes: 24 additions & 11 deletions apps/start/src/routes/_public.onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { SignInGithub } from '@/components/auth/sign-in-github';
import { SignInGoogle } from '@/components/auth/sign-in-google';
import { SignUpEmailForm } from '@/components/auth/sign-up-email-form';
import FullPageLoadingState from '@/components/full-page-loading-state';
import { useOAuthProviders } from '@/hooks/use-oauth-providers';
import { useTRPC } from '@/integrations/trpc/react';
import { createEntityTitle, PAGE_TITLES } from '@/utils/title';

Expand All @@ -28,12 +29,15 @@ export const Route = createFileRoute('/_public/onboarding')({
component: Component,
validateSearch,
loader: async ({ context, location }) => {
await context.queryClient.ensureQueryData(
context.trpc.auth.getOAuthProviders.queryOptions(),
);
const search = validateSearch.safeParse(location.search);
if (search.success && search.data.inviteId) {
await context.queryClient.prefetchQuery(
context.trpc.organization.getInvite.queryOptions({
inviteId: search.data.inviteId,
})
}),
);
}
},
Expand All @@ -43,15 +47,16 @@ export const Route = createFileRoute('/_public/onboarding')({
function Component() {
const { inviteId } = Route.useSearch();
const trpc = useTRPC();
const { providers, hasAny } = useOAuthProviders();
const { data: invite } = useQuery(
trpc.organization.getInvite.queryOptions(
{
inviteId,
},
{
enabled: !!inviteId,
}
)
},
),
);
return (
<div className="col w-full gap-8 py-4 text-left">
Expand Down Expand Up @@ -119,15 +124,23 @@ function Component() {
)}

<div className="space-y-6">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<SignInGithub inviteId={inviteId} type="sign-up" />
<SignInGoogle inviteId={inviteId} type="sign-up" />
</div>
<p className="text-center text-muted-foreground text-xs">
No credit card required · Free 30-day trial · Cancel anytime
</p>
{hasAny && (
<>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
{providers.github && (
<SignInGithub inviteId={inviteId} type="sign-up" />
)}
{providers.google && (
<SignInGoogle inviteId={inviteId} type="sign-up" />
)}
</div>
<p className="text-center text-muted-foreground text-xs">
No credit card required · Free 30-day trial · Cancel anytime
</p>

<Or className="my-6" />
<Or className="my-6" />
</>
)}

<div className="mb-4 flex items-center gap-2 font-semibold text-lg">
<MailIcon className="size-4" />
Expand Down
1 change: 1 addition & 0 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './cookie';
export * from './oauth';
export * from './oauth-providers';
export * from './password';
export * from './session';
export * from './totp';
Loading