Skip to content

[core] Add more OAuth providers to SignInPage #3933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
784bded
feat: More OAuth providers
bharatkashyap Aug 14, 2024
36b962d
fix: CI
bharatkashyap Aug 14, 2024
0434458
fix: Ignore icons for api docs
bharatkashyap Aug 16, 2024
4bcde3d
fix: CI, ignore icons for codecov
bharatkashyap Aug 16, 2024
85a0cf9
Merge branch 'master' into oauth-more-providers
bharatkashyap Aug 17, 2024
7fd0443
Merge branch 'master' into oauth-more-providers
bharatkashyap Aug 18, 2024
543443f
Merge branch 'master' into oauth-more-providers
bharatkashyap Aug 18, 2024
3baf5f9
Merge branch 'master' into oauth-more-providers
bharatkashyap Aug 19, 2024
dfce430
Merge branch 'master' of github.com:mui/mui-toolpad into oauth-more-p…
bharatkashyap Aug 22, 2024
6541d92
fix: Better Cognito icon
bharatkashyap Aug 22, 2024
f0634cc
Merge branch 'master' into oauth-more-providers
bharatkashyap Aug 22, 2024
a09e4a4
Merge branch 'master' of github.com:mui/mui-toolpad into oauth-more-p…
bharatkashyap Aug 27, 2024
7e604c5
fix: Add more providers to CLI
bharatkashyap Aug 27, 2024
0a10ee6
fix: Show list in docs, better variable name
bharatkashyap Aug 28, 2024
7fcdc91
Merge branch 'master' of github.com:mui/mui-toolpad into oauth-more-p…
bharatkashyap Aug 30, 2024
2e5e729
Merge branch 'master' of github.com:mui/mui-toolpad into oauth-more-p…
bharatkashyap Sep 2, 2024
8abe45a
Merge branch 'master' into oauth-more-providers
bharatkashyap Sep 3, 2024
a5e4f43
fix: `create-toolpad-app` depends on `@toolpad/core` types
bharatkashyap Sep 3, 2024
58f731b
fix: Clean templates, fix a bunch of issues in generated apps
bharatkashyap Sep 4, 2024
9c4b999
fix: Warn/error on missing config, misc cleanup to examples/playground
bharatkashyap Sep 4, 2024
84f53cf
Merge branch 'fix/create-toolpad-app' into oauth-more-providers
bharatkashyap Sep 5, 2024
5bf58e1
Merge branch 'master' into oauth-more-providers
bharatkashyap Sep 5, 2024
d9135e5
Merge branch 'master' of github.com:mui/mui-toolpad into oauth-more-p…
bharatkashyap Sep 12, 2024
1bcf42d
fix: Agnostic
bharatkashyap Sep 12, 2024
7e61cae
Merge branch 'master' into oauth-more-providers
bharatkashyap Sep 12, 2024
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
2 changes: 2 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
coverage:
ignore:
- '*/icons/*'
status:
project:
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const providers = [
{ id: 'github', name: 'GitHub' },
{ id: 'google', name: 'Google' },
{ id: 'facebook', name: 'Facebook' },
{ id: 'twitter', name: 'Twitter' },
{ id: 'linkedin', name: 'LinkedIn' },
];

// preview-end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const providers = [
{ id: 'github', name: 'GitHub' },
{ id: 'google', name: 'Google' },
{ id: 'facebook', name: 'Facebook' },
{ id: 'twitter', name: 'Twitter' },
{ id: 'linkedin', name: 'LinkedIn' },
];
// preview-end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const providers = [
{ id: 'github', name: 'GitHub' },
{ id: 'google', name: 'Google' },
{ id: 'facebook', name: 'Facebook' },
{ id: 'twitter', name: 'Twitter' },
{ id: 'linkedin', name: 'LinkedIn' },
];

// ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ The `SignInPage` component is a quick way to generate a ready-to-use authenticat

The `SignInPage` component can be set up with an OAuth provider by passing in a list of providers in the `providers` prop, along with a `signIn` function that accepts the `provider` as a parameter.

{{"demo": "OAuthSignInPage.js", "iframe": true}}
{{"demo": "OAuthSignInPage.js", "iframe": true, "height": 500}}

:::info

Find the list of supported providers in the [API reference](/toolpad/core/api/sign-in-page/#sign-in-page-prop-providers).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels worse. Can't we do the opposite? Keep the list here and just call it a string in the API docs?


Find details on how to set up each provider in the [Auth.js documentation](https://authjs.dev/getting-started/authentication/oauth/).
:::

## Credentials

Expand Down
5 changes: 4 additions & 1 deletion docs/pages/toolpad/core/api/sign-in-page.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"props": {
"providers": {
"type": { "name": "arrayOf", "description": "Array<{ id: string, name: string }>" },
"type": {
"name": "arrayOf",
"description": "Array&lt;{ id: 'apple'<br>&#124;&nbsp;'auth0'<br>&#124;&nbsp;'cognito'<br>&#124;&nbsp;'credentials'<br>&#124;&nbsp;'discord'<br>&#124;&nbsp;'facebook'<br>&#124;&nbsp;'fusionauth'<br>&#124;&nbsp;'github'<br>&#124;&nbsp;'gitlab'<br>&#124;&nbsp;'google'<br>&#124;&nbsp;'instagram'<br>&#124;&nbsp;'keycloak'<br>&#124;&nbsp;'line'<br>&#124;&nbsp;'linkedin'<br>&#124;&nbsp;'microsoft-entra-id'<br>&#124;&nbsp;'okta'<br>&#124;&nbsp;'slack'<br>&#124;&nbsp;'spotify'<br>&#124;&nbsp;'tiktok'<br>&#124;&nbsp;'twitch'<br>&#124;&nbsp;'twitter', name: string }&gt;"
},
"default": "[]"
},
"signIn": {
Expand Down
1 change: 1 addition & 0 deletions packages/create-toolpad-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dependencies": {
"@inquirer/prompts": "^5.3.8",
"@toolpad/utils": "workspace:*",
"@toolpad/core": "workspace:*",
"chalk": "5.3.0",
"execa": "9.3.1",
"invariant": "2.2.4",
Expand Down
4 changes: 3 additions & 1 deletion packages/create-toolpad-app/src/generateProject.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { SupportedAuthProvider } from '@toolpad/core';

// Common files for all apps
import theme from './templates/theme';
import eslintConfig from './templates/eslintConfig';
Expand Down Expand Up @@ -40,7 +42,7 @@ import packageJsonAuthPages from './templates/auth/nextjs-pages/packageJson';
import appAuthPages from './templates/auth/nextjs-pages/app';
import signInPagePagesRouter from './templates/auth/nextjs-pages/signIn';

import { SupportedAuthProvider, SupportedRouter } from './types';
import { SupportedRouter } from './types';

export interface GenerateProjectOptions {
name: string;
Expand Down
21 changes: 20 additions & 1 deletion packages/create-toolpad-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { satisfies } from 'semver';
import { readJsonFile } from '@toolpad/utils/fs';
import invariant from 'invariant';
import { bashResolvePath } from '@toolpad/utils/cli';
import type { SupportedAuthProvider } from '@toolpad/core';
import generateProject, { GenerateProjectOptions } from './generateProject';
import generateStudioProject from './generateStudioProject';
import writeFiles from './writeFiles';
import { downloadAndExtractExample } from './examples';
import type { PackageJson } from './templates/packageType';
import type { SupportedRouter, SupportedAuthProvider, PackageManager } from './types';
import type { SupportedRouter, PackageManager } from './types';

/**
* Find package.json of the create-toolpad-app package
Expand Down Expand Up @@ -297,6 +298,24 @@ const run = async () => {
choices: [
{ name: 'Google', value: 'google' },
{ name: 'GitHub', value: 'github' },
{ name: 'GitLab', value: 'gitlab' },
{ name: 'Twitter', value: 'twitter' },
{ name: 'Facebook', value: 'facebook' },
{ name: 'Cognito', value: 'cognito' },
{ name: 'Microsoft Entra ID', value: 'microsoft-entra-id' },
{ name: 'Apple', value: 'apple' },
{ name: 'Instagram', value: 'instagram' },
{ name: 'TikTok', value: 'tiktok' },
{ name: 'LinkedIn', value: 'linkedin' },
{ name: 'Slack', value: 'slack' },
{ name: 'Spotify', value: 'spotify' },
{ name: 'Twitch', value: 'twitch' },
{ name: 'Discord', value: 'discord' },
{ name: 'Line', value: 'line' },
{ name: 'Auth0', value: 'auth0' },
{ name: 'Keycloak', value: 'keycloak' },
{ name: 'Okta', value: 'okta' },
{ name: 'FusionAuth', value: 'fusionauth' },
],
});
}
Expand Down
15 changes: 9 additions & 6 deletions packages/create-toolpad-app/src/templates/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { kebabToConstant } from '@toolpad/utils/strings';
import { ProvidersTemplate, SupportedAuthProvider } from '../../types';
import type { SupportedAuthProvider } from '@toolpad/core';
import { kebabToConstant, kebabToPascal } from '@toolpad/utils/strings';
import { ProvidersTemplate } from '../../types';
import { requiresIssuer, requiresTenantId } from './utils';

const CredentialsProviderTemplate = `Credentials({
credentials: {
Expand All @@ -18,15 +20,16 @@ const CredentialsProviderTemplate = `Credentials({
},
}),`;

const oAuthProviderTemplate = (provider: SupportedAuthProvider) => `${provider}({
clientId: process.env.${kebabToConstant(provider)}_CLIENT_ID,
clientSecret: process.env.${kebabToConstant(provider)}_CLIENT_SECRET,
const oAuthProviderTemplate = (provider: SupportedAuthProvider) => `${kebabToPascal(provider)}({
clientId: process.env.${kebabToConstant(provider)}_CLIENT_ID,
clientSecret: process.env.${kebabToConstant(provider)}_CLIENT_SECRET,
${requiresIssuer(provider) ? `issuer: process.env.${kebabToConstant(provider)}_ISSUER,\n` : ''}${requiresTenantId(provider) ? `tenantId: process.env.${kebabToConstant(provider)}_TENANT_ID,` : ''}
}),`;

const auth: ProvidersTemplate = (providers) => {
return `import NextAuth from 'next-auth';
${providers
.map((provider) => `import ${provider} from 'next-auth/providers/${provider.toLowerCase()}';`)
.map((provider) => `import ${kebabToPascal(provider)} from 'next-auth/providers/${provider}';`)
.join('\n')}
import type { Provider } from 'next-auth/providers';

Expand Down
13 changes: 8 additions & 5 deletions packages/create-toolpad-app/src/templates/auth/envLocal.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { kebabToConstant } from '@toolpad/utils/strings';
import { ProvidersTemplate } from '../../types';
import { requiresIssuer, requiresTenantId } from './utils';

const env: ProvidersTemplate = (providers) => {
const oAuthProviders = providers.filter((provider) => provider !== 'credentials');
const nonCredentialProviders = providers.filter((provider) => provider !== 'credentials');

return `
# Generate a secret with \`npx auth secret\`
# and replace the value below with it
AUTH_SECRET=secret

# Add the CLIENT_ID and CLIENT_SECRET from your OAuth provider
# to the .env.local file
${oAuthProviders
# Add secrets for your auth providers to the .env.local file

${nonCredentialProviders
.map(
(provider) => `
${kebabToConstant(provider)}_CLIENT_ID=
${kebabToConstant(provider)}_CLIENT_SECRET=`,
${kebabToConstant(provider)}_CLIENT_SECRET=
${requiresIssuer(provider) ? `${kebabToConstant(provider)}_ISSUER=\n` : ''}${requiresTenantId(provider) ? `${kebabToConstant(provider)}_TENANT_ID=\n` : ''}`,
)
.join('\n')}
`;
Expand Down
14 changes: 14 additions & 0 deletions packages/create-toolpad-app/src/templates/auth/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { SupportedAuthProvider } from '@toolpad/core';

export function requiresIssuer(provider: SupportedAuthProvider) {
return (
provider === 'cognito' ||
provider === 'fusionauth' ||
provider === 'keycloak' ||
provider === 'okta'
);
}

export function requiresTenantId(provider: SupportedAuthProvider) {
return provider === 'microsoft-entra-id' || provider === 'fusionauth';
}
8 changes: 2 additions & 6 deletions packages/create-toolpad-app/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { PackageJson } from './templates/packageType';

// https://authjs.dev/reference/core/providers#oauthconfigprofile
type SupportedOAuthProvider = 'facebook' | 'github' | 'google';
import type { SupportedAuthProvider } from '@toolpad/core/SignInPage';

// https://authjs.dev/reference/core/providers#providertype
export type SupportedAuthProvider = SupportedOAuthProvider | 'credentials';
import { PackageJson } from './templates/packageType';

export type SupportedRouter = 'nextjs-app' | 'nextjs-pages';
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
Expand Down
118 changes: 89 additions & 29 deletions packages/toolpad-core/src/SignInPage/SignInPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,76 @@ import LoadingButton, { LoadingButtonProps } from '@mui/lab/LoadingButton';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import GitHubIcon from '@mui/icons-material/GitHub';
import PasswordIcon from '@mui/icons-material/Password';
import FacebookIcon from '@mui/icons-material/Facebook';
import AppleIcon from '@mui/icons-material/Apple';
import Stack from '@mui/material/Stack';
import { LinkProps } from '@mui/material/Link';
import GoogleIcon from './icons/Google';
import FacebookIcon from './icons/Facebook';
import TwitterIcon from './icons/Twitter';
import InstagramIcon from './icons/Instagram';
import TikTokIcon from './icons/TikTok';
import LinkedInIcon from './icons/LinkedIn';
import SlackIcon from './icons/Slack';
import SpotifyIcon from './icons/Spotify';
import TwitchIcon from './icons/Twitch';
import DiscordIcon from './icons/Discord';
import LineIcon from './icons/Line';
import Auth0Icon from './icons/Auth0';
import MicrosoftEntraIdIcon from './icons/MicrosoftEntra';
import CognitoIcon from './icons/Cognito';
import GitLabIcon from './icons/GitLab';
import KeycloakIcon from './icons/Keycloak';
import OktaIcon from './icons/Okta';
import FusionAuthIcon from './icons/FusionAuth';
import { BrandingContext, DocsContext, RouterContext } from '../shared/context';

const IconProviderMap = new Map<string, React.ReactNode>([
type SupportedOAuthProvider =
| 'github'
| 'google'
| 'facebook'
| 'gitlab'
| 'twitter'
| 'apple'
| 'instagram'
| 'tiktok'
| 'linkedin'
| 'slack'
| 'spotify'
| 'twitch'
| 'discord'
| 'line'
| 'auth0'
| 'cognito'
| 'keycloak'
| 'okta'
| 'fusionauth'
| 'microsoft-entra-id';

// https://authjs.dev/reference/core/providers#providertype
export type SupportedAuthProvider = SupportedOAuthProvider | 'credentials';

const IconProviderMap = new Map<SupportedAuthProvider, React.ReactNode>([
['github', <GitHubIcon key="github" />],
['credentials', <PasswordIcon key="credentials" />],
[
'google',
// colored google icon
<svg key="google" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
<path
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
fill="#4285F4"
/>
<path
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
fill="#34A853"
/>
<path
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
fill="#FBBC05"
/>
<path
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
fill="#EA4335"
/>
<path d="M1 1h22v22H1z" fill="none" />
</svg>,
],
['google', <GoogleIcon key="google" />],
['facebook', <FacebookIcon key="facebook" />],
['twitter', <TwitterIcon key="twitter" />],
['apple', <AppleIcon key="apple" />],
['instagram', <InstagramIcon key="instagram" />],
['tiktok', <TikTokIcon key="tiktok" />],
['linkedin', <LinkedInIcon key="linkedin" />],
['slack', <SlackIcon key="slack" />],
['spotify', <SpotifyIcon key="spotify" />],
['twitch', <TwitchIcon key="twitch" />],
['discord', <DiscordIcon key="discord" />],
['line', <LineIcon key="line" />],
['auth0', <Auth0Icon key="auth0" />],
['microsoft-entra-id', <MicrosoftEntraIdIcon key="microsoft-entra-id" />],
['cognito', <CognitoIcon key="cognito" />],
['gitlab', <GitLabIcon key="gitlab" />],
['keycloak', <KeycloakIcon key="keycloak" />],
['okta', <OktaIcon key="okta" />],
['fusionauth', <FusionAuthIcon key="fusionauth" />],
]);

export interface AuthProvider {
Expand All @@ -56,7 +94,7 @@ export interface AuthProvider {
* @example 'google'
* @example 'github'
*/
id: string;
id: SupportedAuthProvider;
/**
* The name of the authentication provider.
* @default ''
Expand Down Expand Up @@ -165,10 +203,10 @@ function SignInPage(props: SignInPageProps) {
const credentialsProvider = providers?.find((provider) => provider.id === 'credentials');
const [{ loading, providerId, error }, setFormStatus] = React.useState<{
loading: boolean;
providerId: string;
providerId?: SupportedAuthProvider;
error?: string;
}>({
providerId: '',
providerId: undefined,
loading: false,
error: '',
});
Expand Down Expand Up @@ -388,7 +426,29 @@ SignInPage.propTypes /* remove-proptypes */ = {
*/
providers: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
id: PropTypes.oneOf([
'apple',
'auth0',
'cognito',
'credentials',
'discord',
'facebook',
'fusionauth',
'github',
'gitlab',
'google',
'instagram',
'keycloak',
'line',
'linkedin',
'microsoft-entra-id',
'okta',
'slack',
'spotify',
'tiktok',
'twitch',
'twitter',
]).isRequired,
name: PropTypes.string.isRequired,
}),
),
Expand Down
17 changes: 17 additions & 0 deletions packages/toolpad-core/src/SignInPage/icons/Auth0.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';

/**
* @ignore - internal component.
*/
function Auth0Icon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 64 64">
<path
d="M49.012 51.774L42.514 32l17.008-12.22h-21.02L32.005 0h21.032l6.506 19.78c3.767 11.468-.118 24.52-10.53 31.993zm-34.023 0L31.998 64l17.015-12.226-17.008-12.22zm-10.516-32c-3.976 12.1.64 24.917 10.5 32.007v-.007L21.482 32 4.474 19.774l21.025.007L31.998 0H10.972z"
fill="#eb5424"
/>
</svg>
);
}

export default Auth0Icon;
Loading
Loading