Add support for auth with Discord (#2110)

This commit is contained in:
wardbox 2024-06-25 09:31:58 -07:00 committed by GitHub
parent 5f63b151cf
commit 3b70f90234
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 787 additions and 24 deletions

View File

@ -19,6 +19,7 @@
},
}
```
- Auth: you can now use Discord as a social auth provider (by @wardbox)
### ⚠️ Breaking Changes & Migration Guide

View File

@ -179,6 +179,9 @@ studio = do
[ [ "usernameAndPassword"
| isJust $ AS.App.Auth.usernameAndPassword methods
],
[ "discord"
| isJust $ AS.App.Auth.discord methods
],
[ "google"
| isJust $ AS.App.Auth.google methods
],

View File

@ -105,6 +105,9 @@ const SocialAuthButtons = styled('div', {
}
})
{=/ isSocialAuthEnabled =}
{=# enabledProviders.isDiscordAuthEnabled =}
const discordSignInUrl = `${config.apiUrl}{= discordSignInPath =}`
{=/ enabledProviders.isDiscordAuthEnabled =}
{=# enabledProviders.isGoogleAuthEnabled =}
const googleSignInUrl = `${config.apiUrl}{= googleSignInPath =}`
{=/ enabledProviders.isGoogleAuthEnabled =}
@ -191,6 +194,9 @@ export const LoginSignupForm = ({
<SocialAuth>
<SocialAuthLabel>{cta} with</SocialAuthLabel>
<SocialAuthButtons gap='large' direction={socialButtonsDirection}>
{=# enabledProviders.isDiscordAuthEnabled =}
<SocialButton href={discordSignInUrl}><SocialIcons.Discord/></SocialButton>
{=/ enabledProviders.isDiscordAuthEnabled =}
{=# enabledProviders.isGoogleAuthEnabled =}
<SocialButton href={googleSignInUrl}><SocialIcons.Google/></SocialButton>
{=/ enabledProviders.isGoogleAuthEnabled =}

View File

@ -54,3 +54,14 @@ export const GitHub = () => (
/>
</svg>
)
export const Discord = () => (
<svg
className={defaultStyles()}
aria-hidden="true"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="M13.545 2.907a13.227 13.227 0 00-3.257-1.011.05.05 0 00-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 00-3.658 0 8.258 8.258 0 00-.412-.833.051.051 0 00-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 00-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 003.995 2.02.05.05 0 00.056-.019c.308-.42.582-.863.818-1.329a.05.05 0 00-.01-.059.051.051 0 00-.018-.011 8.875 8.875 0 01-1.248-.595.05.05 0 01-.02-.066.051.051 0 01.015-.019c.084-.063.168-.129.248-.195a.05.05 0 01.051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 01.053.007c.08.066.164.132.248.195a.051.051 0 01-.004.085 8.254 8.254 0 01-1.249.594.05.05 0 00-.03.03.052.052 0 00.003.041c.24.465.515.909.817 1.329a.05.05 0 00.056.019 13.235 13.235 0 004.001-2.02.049.049 0 00.021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 00-.02-.019zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612z" />
</svg>
)

View File

@ -40,6 +40,7 @@ export type OAuthProviderData = {}
export type PossibleProviderData = {
email: EmailProviderData;
username: UsernameProviderData;
discord: OAuthProviderData;
google: OAuthProviderData;
keycloak: OAuthProviderData;
github: OAuthProviderData;

View File

@ -0,0 +1,2 @@
// PUBLIC API
export { signInUrl as discordSignInUrl } from '../../auth/helpers/Discord'

View File

@ -6,6 +6,9 @@ export * from './email'
{=# isUsernameAndPasswordAuthEnabled =}
export * from './username'
{=/ isUsernameAndPasswordAuthEnabled =}
{=# isDiscordAuthEnabled =}
export * from './discord'
{=/ isDiscordAuthEnabled =}
{=# isGoogleAuthEnabled =}
export * from './google'
{=/ isGoogleAuthEnabled =}

View File

@ -8,6 +8,9 @@ export { VerifyEmailForm } from '../../auth/forms/VerifyEmail'
export { ResetPasswordForm } from '../../auth/forms/ResetPassword'
{=/ isEmailAuthEnabled =}
export type { CustomizationOptions } from '../../auth/forms/types'
{=# isDiscordAuthEnabled =}
export { SignInButton as DiscordSignInButton } from '../../auth/helpers/Discord'
{=/ isDiscordAuthEnabled =}
{=# isGoogleAuthEnabled =}
export { SignInButton as GoogleSignInButton } from '../../auth/helpers/Google'
{=/ isGoogleAuthEnabled =}

View File

@ -37,6 +37,9 @@ export type AuthUserData = Omit<CompleteUserEntityWithAuth, '{= authFieldOnUserE
{=# enabledProviders.isUsernameAndPasswordAuthEnabled =}
username: Expand<UserFacingProviderData<'username'>> | null
{=/ enabledProviders.isUsernameAndPasswordAuthEnabled =}
{=# enabledProviders.isDiscordAuthEnabled =}
discord: Expand<UserFacingProviderData<'discord'>> | null
{=/ enabledProviders.isDiscordAuthEnabled =}
{=# enabledProviders.isGoogleAuthEnabled =}
google: Expand<UserFacingProviderData<'google'>> | null
{=/ enabledProviders.isGoogleAuthEnabled =}
@ -96,6 +99,9 @@ This should never happen, but it did which means there is a bug in the code.`)
{=# enabledProviders.isUsernameAndPasswordAuthEnabled =}
username: getProviderInfo<'username'>({= authFieldOnUserEntityName =}, 'username'),
{=/ enabledProviders.isUsernameAndPasswordAuthEnabled =}
{=# enabledProviders.isDiscordAuthEnabled =}
discord: getProviderInfo<'discord'>({= authFieldOnUserEntityName =}, 'discord'),
{=/ enabledProviders.isDiscordAuthEnabled =}
{=# enabledProviders.isGoogleAuthEnabled =}
google: getProviderInfo<'google'>({= authFieldOnUserEntityName =}, 'google'),
{=/ enabledProviders.isGoogleAuthEnabled =}

View File

@ -0,0 +1,82 @@
{{={= =}=}}
import { Discord } from "arctic";
import type { ProviderConfig } from "wasp/auth/providers/types";
import { getRedirectUriForCallback } from "../oauth/redirect.js";
import { ensureEnvVarsForProvider } from "../oauth/env.js";
import { mergeDefaultAndUserConfig } from "../oauth/config.js";
import { createOAuthProviderRouter } from "../oauth/handler.js";
{=# userSignupFields.isDefined =}
{=& userSignupFields.importStatement =}
const _waspUserSignupFields = {= userSignupFields.importIdentifier =}
{=/ userSignupFields.isDefined =}
{=^ userSignupFields.isDefined =}
const _waspUserSignupFields = undefined
{=/ userSignupFields.isDefined =}
{=# configFn.isDefined =}
{=& configFn.importStatement =}
const _waspUserDefinedConfigFn = {= configFn.importIdentifier =}
{=/ configFn.isDefined =}
{=^ configFn.isDefined =}
const _waspUserDefinedConfigFn = undefined
{=/ configFn.isDefined =}
const _waspConfig: ProviderConfig = {
id: "{= providerId =}",
displayName: "{= displayName =}",
createRouter(provider) {
const env = ensureEnvVarsForProvider(
["DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET"],
provider
);
const discord = new Discord(
env.DISCORD_CLIENT_ID,
env.DISCORD_CLIENT_SECRET,
getRedirectUriForCallback(provider.id).toString(),
);
const config = mergeDefaultAndUserConfig({
scopes: {=& requiredScopes =},
}, _waspUserDefinedConfigFn);
async function getDiscordProfile(accessToken: string): Promise<{
providerProfile: unknown;
providerUserId: string;
}> {
const response = await fetch("https://discord.com/api/users/@me", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const providerProfile = (await response.json()) as {
id?: string;
email?: string;
global_name?: string;
avatar?: string;
};
if (!providerProfile.id) {
throw new Error("Invalid profile");
}
if (providerProfile.avatar) {
providerProfile.avatar = `https://cdn.discordapp.com/avatars/${providerProfile.id}/${providerProfile.avatar}.png`;
}
return { providerProfile, providerUserId: providerProfile.id };
}
return createOAuthProviderRouter({
provider,
oAuthType: 'OAuth2',
userSignupFields: _waspUserSignupFields,
getAuthorizationUrl: ({ state }) => discord.createAuthorizationURL(state, config),
getProviderTokens: ({ code }) => discord.validateAuthorizationCode(code),
getProviderInfo: ({ accessToken }) => getDiscordProfile(accessToken),
});
},
}
export default _waspConfig;

View File

@ -67,7 +67,7 @@
"file",
"../out/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx"
],
"eb12637809476e963828a91896d260f10552c7dd8d7f48f113ca0bfcee7f9ef0"
"0213fcb5830ea8e0ce94d6243c81894d05cceb68199c6ee190eb1b5eba57cabd"
],
[
[
@ -165,7 +165,7 @@
"file",
"../out/sdk/wasp/auth/utils.ts"
],
"4001e5179622ab1c71646ad4225136a16cb9e7df9890c4bfd767c481e86c7619"
"4285ab69fb43af4436b2d8b4f7f330d64036c9ec89a58c29cc700beb2edbb60e"
],
[
[

View File

@ -54,3 +54,14 @@ export const GitHub = () => (
/>
</svg>
)
export const Discord = () => (
<svg
className={defaultStyles()}
aria-hidden="true"
fill="currentColor"
viewBox="0 0 16 16"
>
<path d="M13.545 2.907a13.227 13.227 0 00-3.257-1.011.05.05 0 00-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 00-3.658 0 8.258 8.258 0 00-.412-.833.051.051 0 00-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 00-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 003.995 2.02.05.05 0 00.056-.019c.308-.42.582-.863.818-1.329a.05.05 0 00-.01-.059.051.051 0 00-.018-.011 8.875 8.875 0 01-1.248-.595.05.05 0 01-.02-.066.051.051 0 01.015-.019c.084-.063.168-.129.248-.195a.05.05 0 01.051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 01.053.007c.08.066.164.132.248.195a.051.051 0 01-.004.085 8.254 8.254 0 01-1.249.594.05.05 0 00-.03.03.052.052 0 00.003.041c.24.465.515.909.817 1.329a.05.05 0 00.056.019 13.235 13.235 0 004.001-2.02.049.049 0 00.021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 00-.02-.019zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612z" />
</svg>
)

View File

@ -39,6 +39,7 @@ export type OAuthProviderData = {}
export type PossibleProviderData = {
email: EmailProviderData;
username: UsernameProviderData;
discord: OAuthProviderData;
google: OAuthProviderData;
keycloak: OAuthProviderData;
github: OAuthProviderData;

View File

@ -1,3 +1,4 @@
export declare const Google: () => import("react").JSX.Element;
export declare const Keycloak: () => import("react").JSX.Element;
export declare const GitHub: () => import("react").JSX.Element;
export declare const Discord: () => import("react").JSX.Element;

View File

@ -20,4 +20,7 @@ export const Keycloak = () => (<svg className={defaultStyles()} aria-hidden="tru
export const GitHub = () => (<svg className={defaultStyles()} aria-hidden="true" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clipRule="evenodd"/>
</svg>);
export const Discord = () => (<svg className={defaultStyles()} aria-hidden="true" fill="currentColor" viewBox="0 0 16 16">
<path d="M13.545 2.907a13.227 13.227 0 00-3.257-1.011.05.05 0 00-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 00-3.658 0 8.258 8.258 0 00-.412-.833.051.051 0 00-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 00-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 003.995 2.02.05.05 0 00.056-.019c.308-.42.582-.863.818-1.329a.05.05 0 00-.01-.059.051.051 0 00-.018-.011 8.875 8.875 0 01-1.248-.595.05.05 0 01-.02-.066.051.051 0 01.015-.019c.084-.063.168-.129.248-.195a.05.05 0 01.051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 01.053.007c.08.066.164.132.248.195a.051.051 0 01-.004.085 8.254 8.254 0 01-1.249.594.05.05 0 00-.03.03.052.052 0 00.003.041c.24.465.515.909.817 1.329a.05.05 0 00.056.019 13.235 13.235 0 004.001-2.02.049.049 0 00.021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 00-.02-.019zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612z"/>
</svg>);
//# sourceMappingURL=SocialIcons.jsx.map

View File

@ -1 +1 @@
{"version":3,"file":"SocialIcons.jsx","sourceRoot":"","sources":["../../../../../auth/forms/internal/social/SocialIcons.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAErC,MAAM,aAAa,GAAG,GAAG,CAAC;IACxB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;CAClB,CAAC,CAAA;AAEF,cAAc;AACd,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAC1B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,WAAW,CAEnB;IAAA,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CACxE;MAAA,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC9C;QAAA,CAAC,IAAI,CACH,CAAC,CAAC,sVAAsV,CACxV,EAAE,CAAC,OAAO,CACX,EAAE,IAAI,CACT;MAAA,EAAE,CAAC,CACL;IAAA,EAAE,CAAC,CACL;EAAA,EAAE,GAAG,CAAC,CACP,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CAC5B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,aAAa,CAErB;IAAA,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,iCAAiC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CACzF;MAAA,CAAC,IAAI,CACH,CAAC,CAAC,+iDAA+iD,EAErjD;IAAA,EAAE,CAAC,CACL;EAAA,EAAE,GAAG,CAAC,CACP,CAAA;AAED,cAAc;AACd,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAC1B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,WAAW,CAEnB;IAAA,CAAC,IAAI,CACH,QAAQ,CAAC,SAAS,CAClB,CAAC,CAAC,ktBAAktB,CACptB,QAAQ,CAAC,SAAS,EAEtB;EAAA,EAAE,GAAG,CAAC,CACP,CAAA"}
{"version":3,"file":"SocialIcons.jsx","sourceRoot":"","sources":["../../../../../auth/forms/internal/social/SocialIcons.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAErC,MAAM,aAAa,GAAG,GAAG,CAAC;IACxB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;CAClB,CAAC,CAAA;AAEF,cAAc;AACd,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAC1B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,WAAW,CAEnB;IAAA,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CACxE;MAAA,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC9C;QAAA,CAAC,IAAI,CACH,CAAC,CAAC,sVAAsV,CACxV,EAAE,CAAC,OAAO,CACX,EAAE,IAAI,CACT;MAAA,EAAE,CAAC,CACL;IAAA,EAAE,CAAC,CACL;EAAA,EAAE,GAAG,CAAC,CACP,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CAC5B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,aAAa,CAErB;IAAA,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,iCAAiC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CACzF;MAAA,CAAC,IAAI,CACH,CAAC,CAAC,+iDAA+iD,EAErjD;IAAA,EAAE,CAAC,CACL;EAAA,EAAE,GAAG,CAAC,CACP,CAAA;AAED,cAAc;AACd,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAC1B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,WAAW,CAEnB;IAAA,CAAC,IAAI,CACH,QAAQ,CAAC,SAAS,CAClB,CAAC,CAAC,ktBAAktB,CACptB,QAAQ,CAAC,SAAS,EAEtB;EAAA,EAAE,GAAG,CAAC,CACP,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,CAC3B,CAAC,GAAG,CACF,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC,CAC3B,WAAW,CAAC,MAAM,CAClB,IAAI,CAAC,cAAc,CACnB,OAAO,CAAC,WAAW,CAEnB;IAAA,CAAC,IAAI,CAAC,CAAC,CAAC,0kCAA0kC,EACplC;EAAA,EAAE,GAAG,CAAC,CACP,CAAA"}

View File

@ -21,6 +21,7 @@ export type OAuthProviderData = {};
export type PossibleProviderData = {
email: EmailProviderData;
username: UsernameProviderData;
discord: OAuthProviderData;
google: OAuthProviderData;
keycloak: OAuthProviderData;
github: OAuthProviderData;

View File

@ -1 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../auth/utils.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../auth/utils.ts"],"names":[],"mappings":"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"}

View File

@ -18,3 +18,7 @@ GITHUB_CLIENT_SECRET='dummy-gh-client-secret'
# KEYCLOAK_CLIENT_ID='dummy-id'
# KEYCLOAK_CLIENT_SECRET='dummy-secret'
# KEYCLOAK_REALM_URL='dummy-url'
# Dummy values here will allow app to run, but you will need real values to get Discord Auth to work.
DISCORD_CLIENT_SECRET='dummy-discord-client-secret'
DISCORD_CLIENT_ID='dummy-discord-client-id'

View File

@ -14,6 +14,10 @@ app todoApp {
// usernameAndPassword: {
// userSignupFields: import { userSignupFields } from "@src/auth/github",
// },
discord: {
configFn: import { config } from "@src/auth/discord",
userSignupFields: import { userSignupFields } from "@src/auth/discord"
},
google: {
configFn: import { config } from "@src/auth/google",
userSignupFields: import { userSignupFields } from "@src/auth/google"

View File

@ -0,0 +1,10 @@
import { defineUserSignupFields } from 'wasp/server/auth'
export function config() {
console.log('Inside user-supplied Discord config')
return {
scopes: ['identify', 'email'],
}
}
export const userSignupFields = defineUserSignupFields({})

View File

@ -1,3 +1,3 @@
GOOGLE_CLIENT_ID="mock-client-id"
GOOGLE_CLIENT_SECRET="mock-client-secret"
SKIP_EMAIL_VERIFICATION_IN_DEV=true
SKIP_EMAIL_VERIFICATION_IN_DEV=true

View File

@ -9,6 +9,7 @@ module Wasp.AppSpec.App.Auth
UsernameAndPasswordConfig (..),
isUsernameAndPasswordAuthEnabled,
isExternalAuthEnabled,
isDiscordAuthEnabled,
isGoogleAuthEnabled,
isKeycloakAuthEnabled,
isGitHubAuthEnabled,
@ -42,6 +43,7 @@ data Auth = Auth
data AuthMethods = AuthMethods
{ usernameAndPassword :: Maybe UsernameAndPasswordConfig,
discord :: Maybe ExternalAuthConfig,
google :: Maybe ExternalAuthConfig,
gitHub :: Maybe ExternalAuthConfig,
keycloak :: Maybe ExternalAuthConfig,
@ -76,11 +78,15 @@ isExternalAuthEnabled auth =
any
($ auth)
-- NOTE: Make sure to add new external auth methods here.
[ isGoogleAuthEnabled,
[ isDiscordAuthEnabled,
isGoogleAuthEnabled,
isGitHubAuthEnabled,
isKeycloakAuthEnabled
]
isDiscordAuthEnabled :: Auth -> Bool
isDiscordAuthEnabled = isJust . discord . methods
isGoogleAuthEnabled :: Auth -> Bool
isGoogleAuthEnabled = isJust . google . methods

View File

@ -47,10 +47,19 @@ emailAuthProvider =
E._displayName = "Email and password"
}
discordAuthProvider :: OA.OAuthAuthProvider
discordAuthProvider =
OA.OAuthAuthProvider
{ OA._providerId = fromJust $ makeProviderId "discord",
OA._displayName = "Discord",
OA._requiredScope = ["identify"]
}
getEnabledAuthProvidersJson :: AS.Auth.Auth -> Aeson.Value
getEnabledAuthProvidersJson auth =
object
[ "isGoogleAuthEnabled" .= AS.Auth.isGoogleAuthEnabled auth,
[ "isDiscordAuthEnabled" .= AS.Auth.isDiscordAuthEnabled auth,
"isGoogleAuthEnabled" .= AS.Auth.isGoogleAuthEnabled auth,
"isKeycloakAuthEnabled" .= AS.Auth.isKeycloakAuthEnabled auth,
"isGitHubAuthEnabled" .= AS.Auth.isGitHubAuthEnabled auth,
"isUsernameAndPasswordAuthEnabled" .= AS.Auth.isUsernameAndPasswordAuthEnabled auth,

View File

@ -7,7 +7,8 @@ import Data.Aeson (object, (.=))
import StrongPath (reldir, relfile, (</>))
import qualified Wasp.AppSpec.App.Auth as AS.Auth
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
( discordAuthProvider,
gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
@ -119,6 +120,7 @@ genLoginSignupForm auth =
"areBothSocialAndPasswordBasedAuthEnabled" .= areBothSocialAndPasswordBasedAuthEnabled,
"isAnyPasswordBasedAuthEnabled" .= isAnyPasswordBasedAuthEnabled,
"isSocialAuthEnabled" .= AS.Auth.isExternalAuthEnabled auth,
"discordSignInPath" .= OAuth.serverLoginUrl discordAuthProvider,
"googleSignInPath" .= OAuth.serverLoginUrl googleAuthProvider,
"keycloakSignInPath" .= OAuth.serverLoginUrl keycloakAuthProvider,
"gitHubSignInPath" .= OAuth.serverLoginUrl gitHubAuthProvider,

View File

@ -8,7 +8,8 @@ import StrongPath (File', Path', Rel', reldir, relfile)
import qualified StrongPath as SP
import qualified Wasp.AppSpec.App.Auth as AS.Auth
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
( discordAuthProvider,
gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
@ -28,11 +29,13 @@ genHelpers :: AS.Auth.Auth -> Generator [FileDraft]
genHelpers auth =
return $
concat
[ [gitHubHelpers | AS.Auth.isGitHubAuthEnabled auth],
[ [discordHelpers | AS.Auth.isDiscordAuthEnabled auth],
[gitHubHelpers | AS.Auth.isGitHubAuthEnabled auth],
[googleHelpers | AS.Auth.isGoogleAuthEnabled auth],
[keycloakHelpers | AS.Auth.isKeycloakAuthEnabled auth]
]
where
discordHelpers = mkHelpersFd discordAuthProvider [relfile|Discord.tsx|]
gitHubHelpers = mkHelpersFd gitHubAuthProvider [relfile|GitHub.tsx|]
googleHelpers = mkHelpersFd googleAuthProvider [relfile|Google.tsx|]
keycloakHelpers = mkHelpersFd keycloakAuthProvider [relfile|Keycloak.tsx|]

View File

@ -26,6 +26,7 @@ genNewClientAuth spec =
]
<++> genAuthEmail auth
<++> genAuthUsername auth
<++> genAuthDiscord auth
<++> genAuthGoogle auth
<++> genAuthKeycloak auth
<++> genAuthGitHub auth
@ -62,6 +63,12 @@ genAuthUsername auth =
then sequence [genFileCopy [relfile|client/auth/username.ts|]]
else return []
genAuthDiscord :: AS.Auth.Auth -> Generator [FileDraft]
genAuthDiscord auth =
if AS.Auth.isDiscordAuthEnabled auth
then sequence [genFileCopy [relfile|client/auth/discord.ts|]]
else return []
genAuthGoogle :: AS.Auth.Auth -> Generator [FileDraft]
genAuthGoogle auth =
if AS.Auth.isGoogleAuthEnabled auth

View File

@ -27,7 +27,8 @@ import qualified Wasp.AppSpec.App.Auth as AS.Auth
import qualified Wasp.AppSpec.App.Dependency as App.Dependency
import Wasp.AppSpec.Valid (getApp)
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
( discordAuthProvider,
gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
@ -52,6 +53,7 @@ genOAuthAuth :: AS.Auth.Auth -> Generator [FileDraft]
genOAuthAuth auth
| AS.Auth.isExternalAuthEnabled auth =
genOAuthHelpers auth
<++> genOAuthProvider discordAuthProvider (AS.Auth.discord . AS.Auth.methods $ auth)
<++> genOAuthProvider googleAuthProvider (AS.Auth.google . AS.Auth.methods $ auth)
<++> genOAuthProvider keycloakAuthProvider (AS.Auth.keycloak . AS.Auth.methods $ auth)
<++> genOAuthProvider gitHubAuthProvider (AS.Auth.gitHub . AS.Auth.methods $ auth)

View File

@ -24,7 +24,8 @@ import qualified Wasp.AppSpec.App.Auth as AS.Auth
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
import Wasp.AppSpec.Valid (getApp)
import Wasp.Generator.AuthProviders
( emailAuthProvider,
( discordAuthProvider,
emailAuthProvider,
gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
@ -85,7 +86,8 @@ genProvidersIndex auth = return $ C.mkTmplFdWithData [relfile|src/auth/providers
providers =
makeConfigImportJson
<$> concat
[ [OAuthProvider.providerId gitHubAuthProvider | AS.Auth.isGitHubAuthEnabled auth],
[ [OAuthProvider.providerId discordAuthProvider | AS.Auth.isDiscordAuthEnabled auth],
[OAuthProvider.providerId gitHubAuthProvider | AS.Auth.isGitHubAuthEnabled auth],
[OAuthProvider.providerId googleAuthProvider | AS.Auth.isGoogleAuthEnabled auth],
[OAuthProvider.providerId keycloakAuthProvider | AS.Auth.isKeycloakAuthEnabled auth],
[LocalProvider.providerId localAuthProvider | AS.Auth.isUsernameAndPasswordAuthEnabled auth],

View File

@ -143,6 +143,7 @@ spec_Analyzer = do
Auth.UsernameAndPasswordConfig
{ Auth.userSignupFields = Just $ ExtImport (ExtImportField "getUserFields") (fromJust $ SP.parseRelFileP "auth/signup.js")
},
Auth.discord = Nothing,
Auth.google = Nothing,
Auth.keycloak = Nothing,
Auth.gitHub = Nothing,

View File

@ -109,6 +109,7 @@ spec_AppSpecValid = do
AS.Auth.methods =
AS.Auth.AuthMethods
{ AS.Auth.usernameAndPassword = Just AS.Auth.UsernameAndPasswordConfig {AS.Auth.userSignupFields = Nothing},
AS.Auth.discord = Nothing,
AS.Auth.google = Nothing,
AS.Auth.gitHub = Nothing,
AS.Auth.keycloak = Nothing,
@ -199,7 +200,7 @@ spec_AppSpecValid = do
}
it "returns no error if app.auth is not set" $ do
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing, email = Nothing}) validUserEntity) `shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = Nothing, discord = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing, email = Nothing}) validUserEntity) `shouldBe` []
it "returns no error if app.auth is set and only one of UsernameAndPassword and Email is used" $ do
ASV.validateAppSpec
@ -210,6 +211,7 @@ spec_AppSpecValid = do
AS.Auth.UsernameAndPasswordConfig
{ AS.Auth.userSignupFields = Nothing
},
discord = Nothing,
google = Nothing,
keycloak = Nothing,
gitHub = Nothing,
@ -219,7 +221,7 @@ spec_AppSpecValid = do
validUserEntity
)
`shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing, email = Just emailAuthConfig}) validUserEntity) `shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = Nothing, discord = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing, email = Just emailAuthConfig}) validUserEntity) `shouldBe` []
it "returns an error if app.auth is set and both UsernameAndPassword and Email are used" $ do
ASV.validateAppSpec
@ -230,6 +232,7 @@ spec_AppSpecValid = do
AS.Auth.UsernameAndPasswordConfig
{ AS.Auth.userSignupFields = Nothing
},
discord = Nothing,
google = Nothing,
keycloak = Nothing,
gitHub = Nothing,
@ -297,7 +300,7 @@ spec_AppSpecValid = do
Just
AS.Auth.Auth
{ AS.Auth.methods =
AS.Auth.AuthMethods {email = Just emailAuthConfig, usernameAndPassword = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing},
AS.Auth.AuthMethods {email = Just emailAuthConfig, usernameAndPassword = Nothing, discord = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing},
AS.Auth.userEntity = AS.Core.Ref.Ref userEntityName,
AS.Auth.externalAuthEntity = Nothing,
AS.Auth.onAuthFailedRedirectTo = "/",

View File

@ -1,6 +1,7 @@
:root {
--auth-pills-color: #333;
--auth-pills-email: #e0f2fe;
--auth-pills-discord: #d3d6f2;
--auth-pills-github: #f1f5f9;
--auth-pills-google: #ecfccb;
--auth-pills-keycloak: #d0ebf5;
@ -10,6 +11,7 @@
:root[data-theme='dark'] {
--auth-pills-color: #fff;
--auth-pills-email: #0c4a6e;
--auth-pills-discord: #2f3670;
--auth-pills-github: #334155;
--auth-pills-google: #365314;
--auth-pills-keycloak: #2d5866;

View File

@ -46,6 +46,19 @@ export function UsernameAndPasswordPill() {
)
}
export function DiscordPill() {
return (
<Pill
style={{
backgroundColor: 'var(--auth-pills-discord)',
}}
linkToPage="/docs/auth/social-auth/discord"
>
Discord
</Pill>
)
}
export function GithubPill() {
return (
<Pill

View File

@ -2,7 +2,7 @@
title: Auth Hooks
---
import { EmailPill, UsernameAndPasswordPill, GithubPill, GooglePill, KeycloakPill } from "./Pills";
import { EmailPill, UsernameAndPasswordPill, GithubPill, GooglePill, KeycloakPill, DiscordPill } from "./Pills";
import ImgWithCaption from '@site/blog/components/ImgWithCaption'
Auth hooks allow you to "hook into" the auth process at various stages and run your custom code. For example, if you want to forbid certain emails from signing up, or if you wish to send a welcome email to the user after they sign up, auth hooks are the way to go.
@ -87,7 +87,7 @@ Wasp calls the `onBeforeSignup` hook before the user is created.
The `onBeforeSignup` hook can be useful if you want to reject a user based on some criteria before they sign up.
Works with <EmailPill /> <UsernameAndPasswordPill /> <GithubPill /> <GooglePill /> <KeycloakPill />
Works with <EmailPill /> <UsernameAndPasswordPill /> <DiscordPill /> <GithubPill /> <GooglePill /> <KeycloakPill />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
@ -175,7 +175,7 @@ The `onAfterSignup` hook can be useful if you want to send the user a welcome em
Since the `onAfterSignup` hook receives the OAuth access token, it can also be used to store the OAuth access token for the user in your database.
Works with <EmailPill /> <UsernameAndPasswordPill /> <GithubPill /> <GooglePill /> <KeycloakPill />
Works with <EmailPill /> <UsernameAndPasswordPill /> <DiscordPill /> <GithubPill /> <GooglePill /> <KeycloakPill />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
@ -270,7 +270,7 @@ Wasp calls the `onBeforeOAuthRedirect` hook after the OAuth redirect URL is gene
The `onBeforeOAuthRedirect` hook can be useful if you want to save some data (e.g. request query parameters) that can be used later in the OAuth flow. You can use the `uniqueRequestId` parameter to reference this data later in the `onAfterSignup` hook.
Works with <GithubPill /> <GooglePill /> <KeycloakPill />
Works with <DiscordPill /> <GithubPill /> <GooglePill /> <KeycloakPill />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">

View File

@ -21,6 +21,11 @@ export function SocialAuthGrid({
description: 'Users sign in with their Keycloak account.',
linkToDocs: '/docs/auth/social-auth/keycloak' + pagePart,
},
{
title: 'Discord',
description: 'Users sign in with their Discord account.',
linkToDocs: '/docs/auth/social-auth/discord' + pagePart,
},
]
return (
<>

View File

@ -0,0 +1,549 @@
---
title: Discord
---
import useBaseUrl from '@docusaurus/useBaseUrl';
import DefaultBehaviour from './\_default-behaviour.md';
import OverrideIntro from './\_override-intro.md';
import OverrideExampleIntro from './\_override-example-intro.md';
import UsingAuthNote from './\_using-auth-note.md';
import WaspFileStructureNote from './\_wasp-file-structure-note.md';
import GetUserFieldsType from './\_getuserfields-type.md';
import ApiReferenceIntro from './\_api-reference-intro.md';
import UserSignupFieldsExplainer from '../\_user-signup-fields-explainer.md';
Wasp supports Discord Authentication out of the box.
Letting your users log in using their Discord accounts turns the signup process into a breeze.
Let's walk through enabling Discord Authentication, explain some of the default settings, and show how to override them.
## Setting up Discord Auth
Enabling Discord Authentication comes down to a series of steps:
1. Enabling Discord authentication in the Wasp file.
1. Adding the `User` entity.
1. Creating a Discord App.
1. Adding the necessary Routes and Pages
1. Using Auth UI components in our Pages.
<WaspFileStructureNote />
### 1. Adding Discord Auth to Your Wasp File
Let's start by properly configuring the Auth object:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
// highlight-next-line
// 1. Specify the User entity (we'll define it next)
// highlight-next-line
userEntity: User,
methods: {
// highlight-next-line
// 2. Enable Discord Auth
// highlight-next-line
discord: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
// highlight-next-line
// 1. Specify the User entity (we'll define it next)
// highlight-next-line
userEntity: User,
methods: {
// highlight-next-line
// 2. Enable Discord Auth
// highlight-next-line
discord: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
### 2. Add the User Entity
Let's now define the `app.auth.userEntity` entity:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
// ...
// 3. Define the User entity
// highlight-next-line
entity User {=psl
id Int @id @default(autoincrement())
// ...
psl=}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
// ...
// 3. Define the User entity
// highlight-next-line
entity User {=psl
id Int @id @default(autoincrement())
// ...
psl=}
```
</TabItem>
</Tabs>
### 3. Creating a Discord App
To use Discord as an authentication method, you'll first need to create a Discord App and provide Wasp with your client key and secret. Here's how you do it:
1. Log into your Discord account and navigate to: https://discord.com/developers/applications.
2. Select **New Application**.
3. Supply required information.
<img alt="Discord Applications Screenshot"
src={useBaseUrl('img/integrations-discord-1.png')}
width="400px"
/>
4. Go to the **OAuth2** tab on the sidebar and click **Add Redirect**
- For development, put: `http://localhost:3001/auth/discord/callback`.
- Once you know on which URL your API server will be deployed, you can create a new app with that URL instead e.g. `https://your-server-url.com/auth/discord/callback`.
4. Hit **Save Changes**.
5. Hit **Reset Secret**
6. Copy your Client ID and Client secret as you'll need them in the next step.
### 4. Adding Environment Variables
Add these environment variables to the `.env.server` file at the root of your project (take their values from the previous step):
```bash title=".env.server"
DISCORD_CLIENT_ID=your-discord-client-id
DISCORD_CLIENT_SECRET=your-discord-client-secret
```
### 5. Adding the Necessary Routes and Pages
Let's define the necessary authentication Routes and Pages.
Add the following code to your `main.wasp` file:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
// ...
// 6. Define the routes
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
component: import { Login } from "@src/pages/auth.jsx"
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
// ...
// 6. Define the routes
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
component: import { Login } from "@src/pages/auth.tsx"
}
```
</TabItem>
</Tabs>
We'll define the React components for these pages in the `src/pages/auth.{jsx,tsx}` file below.
### 6. Creating the Client Pages
:::info
We are using [Tailwind CSS](https://tailwindcss.com/) to style the pages. Read more about how to add it [here](../../project/css-frameworks).
:::
Let's create a `auth.{jsx,tsx}` file in the `src/pages` folder and add the following to it:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```tsx title="src/pages/auth.jsx"
import { LoginForm } from 'wasp/client/auth'
export function Login() {
return (
<Layout>
<LoginForm />
</Layout>
)
}
// A layout component to center the content
export function Layout({ children }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>{children}</div>
</div>
</div>
</div>
)
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```tsx title="src/pages/auth.tsx"
import { LoginForm } from 'wasp/client/auth'
export function Login() {
return (
<Layout>
<LoginForm />
</Layout>
)
}
// A layout component to center the content
export function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>{children}</div>
</div>
</div>
</div>
)
}
```
</TabItem>
</Tabs>
We imported the generated Auth UI components and used them in our pages. Read more about the Auth UI components [here](../../auth/ui).
### Conclusion
Yay, we've successfully set up Discord Auth! 🎉
![Discord Auth](/img/auth/discord.png)
Running `wasp db migrate-dev` and `wasp start` should now give you a working app with authentication.
To see how to protect specific pages (i.e., hide them from non-authenticated users), read the docs on [using auth](../../auth/overview).
## Default Behaviour
Add `discord: {}` to the `auth.methods` dictionary to use it with default settings.
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
// highlight-next-line
discord: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
// highlight-next-line
discord: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
<DefaultBehaviour />
## Overrides
<OverrideIntro />
### Data Received From Discord
We are using Discord's API and its `/users/@me` endpoint to get the user data.
The data we receive from Discord on the `/users/@me` endpoint looks something this:
```json
{
"id": "80351110224678912",
"username": "Nelly",
"discriminator": "1337",
"avatar": "8342729096ea3675442027381ff50dfe",
"verified": true,
"flags": 64,
"banner": "06c16474723fe537c283b8efa61a30c8",
"accent_color": 16711680,
"premium_type": 1,
"public_flags": 64,
"avatar_decoration_data": {
"sku_id": "1144058844004233369",
"asset": "a_fed43ab12698df65902ba06727e20c0e"
}
}
```
The fields you receive will depend on the scopes you requested. The default scope is set to `identify` only. If you want to get the email, you need to specify the `email` scope in the `configFn` function.
<small>
For an up to date info about the data received from Discord, please refer to the [Discord API documentation](https://discord.com/developers/docs/resources/user#user-object-user-structure).
</small>
### Using the Data Received From Discord
<OverrideExampleIntro />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
discord: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/discord.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/discord.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
entity User {=psl
id Int @id @default(autoincrement())
username String @unique
avatarUrl String
psl=}
// ...
```
```js title=src/auth/discord.js
export const userSignupFields = {
username: (data) => data.profile.global_name,
avatarUrl: (data) => data.profile.avatar,
};
export function getConfig() {
return {
scopes: ['identify'],
};
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
discord: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/discord.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/discord.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
entity User {=psl
id Int @id @default(autoincrement())
username String @unique
avatarUrl String
psl=}
// ...
```
```ts title=src/auth/discord.ts
import { defineUserSignupFields } from 'wasp/server/auth'
export const userSignupFields = defineUserSignupFields({
username: (data: any) => data.profile.global_name,
avatarUrl: (data: any) => data.profile.avatar,
})
export function getConfig() {
return {
scopes: ['identify'],
}
}
```
<GetUserFieldsType />
</TabItem>
</Tabs>
## Using Auth
<UsingAuthNote />
## API Reference
<ApiReferenceIntro />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
discord: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/discord.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/discord.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.14.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
discord: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/discord.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/discord.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
The `discord` dict has the following properties:
- #### `configFn: ExtImport`
This function should return an object with the scopes for the OAuth provider.
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```js title=src/auth/discord.js
export function getConfig() {
return {
scopes: [],
}
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```ts title=src/auth/discord.ts
export function getConfig() {
return {
scopes: [],
}
}
```
</TabItem>
</Tabs>
- #### `userSignupFields: ExtImport`
<UserSignupFieldsExplainer />
Read more about the `userSignupFields` function [here](../overview#1-defining-extra-fields).

View File

@ -2,7 +2,7 @@
title: Auth UI
---
import { EmailPill, UsernameAndPasswordPill, GithubPill, GooglePill, KeycloakPill } from "./Pills";
import { EmailPill, UsernameAndPasswordPill, GithubPill, GooglePill, KeycloakPill, DiscordPill } from "./Pills";
To make using authentication in your app as easy as possible, Wasp generates the server-side code but also the client-side UI for you. It enables you to quickly get the login, signup, password reset and email verification flows in your app.
@ -110,7 +110,7 @@ The following components are available for you to use in your app:
### Login Form
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill /> and <KeycloakPill /> authentication.
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill />, <KeycloakPill />, and <DiscordPill /> authentication.
![Login form](/img/authui/login.png)
@ -165,7 +165,7 @@ It will automatically show the correct authentication providers based on your `m
### Signup Form
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill /> and <KeycloakPill /> authentication.
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill />, <KeycloakPill />, and <DiscordPill /> authentication.
![Signup form](/img/authui/signup.png)

View File

@ -71,6 +71,7 @@ module.exports = {
'auth/social-auth/github',
'auth/social-auth/google',
'auth/social-auth/keycloak',
'auth/social-auth/discord',
],
},
'auth/entities/entities',

View File

@ -27,7 +27,12 @@ export function AuthMethodsGrid() {
title: 'Keycloak',
description: 'Users sign in with their Keycloak account',
linkToDocs: '/docs/auth/social-auth/keycloak',
}
},
{
title: 'Discord',
description: 'Users sign in with their Discord account',
linkToDocs: '/docs/auth/social-auth/discord',
},
]
return (
<>

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB