Adds Keycloak OAuth provider (#1876)

This commit is contained in:
Mihovil Ilakovac 2024-03-18 13:09:08 +01:00
parent 3d5ed1a5d3
commit 0880556592
42 changed files with 792 additions and 27 deletions

View File

@ -182,6 +182,9 @@ studio = do
[ "google"
| isJust $ AS.App.Auth.google methods
],
[ "keycloak"
| isJust $ AS.App.Auth.keycloak methods
],
[ "gitHub"
| isJust $ AS.App.Auth.gitHub methods
],

View File

@ -108,6 +108,9 @@ const SocialAuthButtons = styled('div', {
{=# isGoogleAuthEnabled =}
const googleSignInUrl = `${config.apiUrl}{= googleSignInPath =}`
{=/ isGoogleAuthEnabled =}
{=# isKeycloakAuthEnabled =}
const keycloakSignInUrl = `${config.apiUrl}{= keycloakSignInPath =}`
{=/ isKeycloakAuthEnabled =}
{=# isGitHubAuthEnabled =}
const gitHubSignInUrl = `${config.apiUrl}{= gitHubSignInPath =}`
{=/ isGitHubAuthEnabled =}
@ -192,6 +195,10 @@ export const LoginSignupForm = ({
<SocialButton href={googleSignInUrl}><SocialIcons.Google/></SocialButton>
{=/ isGoogleAuthEnabled =}
{=# isKeycloakAuthEnabled =}
<SocialButton href={keycloakSignInUrl}><SocialIcons.Keycloak/></SocialButton>
{=/ isKeycloakAuthEnabled =}
{=# isGitHubAuthEnabled =}
<SocialButton href={gitHubSignInUrl}><SocialIcons.GitHub/></SocialButton>
{=/ isGitHubAuthEnabled =}

View File

@ -24,6 +24,21 @@ export const Google = () => (
</svg>
)
export const Keycloak = () => (
<svg
className={defaultStyles()}
aria-hidden="true"
fill="currentColor"
viewBox="0 0 559 466"
>
<g id="brand" transform="matrix(1,0,0,1,-233.075,-279.1)" fill="#000000" fillRule="nonzero">
<path
d="M786.2,395.5L705.6,395.5C704.1,395.5 702.6,394.7 701.9,393.4L637.2,281.2C636.4,279.9 635,279.1 633.4,279.1L369.4,279.1C367.9,279.1 366.4,279.9 365.7,281.2L298.4,397.6L233.6,509.8C232.9,511.1 232.9,512.7 233.6,514.1L298.4,626.3L365.6,742.8C366.3,744.1 367.8,745 369.3,744.9L633.4,744.9C634.9,744.9 636.4,744.1 637.2,742.8L702,630.6C702.7,629.3 704.2,628.4 705.7,628.5L786.3,628.5C789,628.5 791.1,626.3 791.1,623.7L791.1,400.4C791,397.7 788.8,395.5 786.2,395.5ZM477.5,630.6L457.2,665.6C456.9,666.1 456.4,666.6 455.9,666.9C455.3,667.2 454.7,667.4 454,667.4L413.7,667.4C412.3,667.4 411,666.7 410.4,665.4L350.3,561.1L344.4,550.8L322.8,513.9C322.5,513.4 322.3,512.8 322.4,512.1C322.4,511.5 322.6,510.8 322.9,510.3L344.6,472.7L410.5,358.7C411.2,357.5 412.5,356.7 413.8,356.7L454,356.7C454.7,356.7 455.4,356.9 456.1,357.2C456.6,357.5 457.1,357.9 457.4,358.5L477.7,393.7C478.3,394.9 478.2,396.4 477.5,397.5L412.4,510.3C412.1,510.8 412,511.4 412,511.9C412,512.5 412.2,513 412.4,513.5L477.5,626.2C478.4,627.7 478.3,629.3 477.5,630.6ZM679.6,513.9L658,550.8L652.1,561.1L592,665.4C591.3,666.6 590.1,667.4 588.7,667.4L548.4,667.4C547.7,667.4 547.1,667.2 546.5,666.9C546,666.6 545.5,666.2 545.2,665.6L524.9,630.6C524,629.3 524,627.7 524.8,626.4L589.9,513.7C590.2,513.2 590.3,512.6 590.3,512.1C590.3,511.5 590.1,511 589.9,510.5L524.8,397.7C524.1,396.5 524,395.1 524.6,393.9L544.9,358.7C545.2,358.2 545.7,357.7 546.2,357.4C546.8,357 547.5,356.9 548.3,356.9L588.7,356.9C590.1,356.9 591.4,357.6 592,358.9L657.9,472.9L679.6,510.5C679.9,511.1 680.1,511.7 680.1,512.3C680.1,512.7 679.9,513.3 679.6,513.9Z"
/>
</g>
</svg>
)
// PRIVATE API
export const GitHub = () => (
<svg

View File

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

View File

@ -9,6 +9,9 @@ export * from './username'
{=# isGoogleAuthEnabled =}
export * from './google'
{=/ isGoogleAuthEnabled =}
{=# isKeycloakAuthEnabled =}
export * from './keycloak'
{=/ isKeycloakAuthEnabled =}
{=# isGitHubAuthEnabled =}
export * from './github'
{=/ isGitHubAuthEnabled =}

View File

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

View File

@ -0,0 +1,82 @@
{{={= =}=}}
import { Keycloak } 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(
["KEYCLOAK_REALM_URL", "KEYCLOAK_CLIENT_ID", "KEYCLOAK_CLIENT_SECRET"],
provider
);
const keycloak = new Keycloak(
env.KEYCLOAK_REALM_URL,
env.KEYCLOAK_CLIENT_ID,
env.KEYCLOAK_CLIENT_SECRET,
getRedirectUriForCallback(provider.id).toString(),
);
const config = mergeDefaultAndUserConfig({
scopes: {=& requiredScopes =},
}, _waspUserDefinedConfigFn);
async function getKeycloakProfile(accessToken: string): Promise<{
providerProfile: unknown;
providerUserId: string;
}> {
const userInfoEndpoint = `${env.KEYCLOAK_REALM_URL}/protocol/openid-connect/userinfo`;
const response = await fetch(
userInfoEndpoint,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
const providerProfile = (await response.json()) as {
sub?: string;
};
if (!providerProfile.sub) {
throw new Error("Invalid profile");
}
return { providerProfile, providerUserId: providerProfile.sub };
}
return createOAuthProviderRouter({
provider,
stateTypes: ['state', 'codeVerifier'],
userSignupFields: _waspUserSignupFields,
getAuthorizationUrl: ({ state, codeVerifier }) => keycloak.createAuthorizationURL(state, codeVerifier, config),
getProviderInfo: async ({ code, codeVerifier }) => {
const { accessToken } = await keycloak.validateAuthorizationCode(code, codeVerifier);
return getKeycloakProfile(accessToken);
},
});
},
}
export default _waspConfig;

View File

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

View File

@ -53,7 +53,7 @@
"file",
"../out/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx"
],
"cba000c7dedb0e2f1c9730719e23215036a599bd58e7590adc5cdb176ac6b76b"
"c3299b01607b1206208e3e24a2830932607e8783a660b51e8f9168cbb067aab1"
],
[
[
@ -67,7 +67,7 @@
"file",
"../out/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx"
],
"bca4b3d9bc1cd57bdc83ad51be235ac5a5f220e8b63a91dcce6da296f32f51c6"
"eb12637809476e963828a91896d260f10552c7dd8d7f48f113ca0bfcee7f9ef0"
],
[
[
@ -165,7 +165,7 @@
"file",
"../out/sdk/wasp/auth/utils.ts"
],
"014159c084202532b1da84bcd945f77960fe1f7acd383afc905e8fbea35b9d71"
"4001e5179622ab1c71646ad4225136a16cb9e7df9890c4bfd767c481e86c7619"
],
[
[

View File

@ -93,6 +93,7 @@ export const LoginSignupForm = ({
<SocialAuthButtons gap='large' direction={socialButtonsDirection}>
<SocialButton href={googleSignInUrl}><SocialIcons.Google/></SocialButton>
</SocialAuthButtons>
</SocialAuth>
</>)

View File

@ -24,6 +24,21 @@ export const Google = () => (
</svg>
)
export const Keycloak = () => (
<svg
className={defaultStyles()}
aria-hidden="true"
fill="currentColor"
viewBox="0 0 559 466"
>
<g id="brand" transform="matrix(1,0,0,1,-233.075,-279.1)" fill="#000000" fillRule="nonzero">
<path
d="M786.2,395.5L705.6,395.5C704.1,395.5 702.6,394.7 701.9,393.4L637.2,281.2C636.4,279.9 635,279.1 633.4,279.1L369.4,279.1C367.9,279.1 366.4,279.9 365.7,281.2L298.4,397.6L233.6,509.8C232.9,511.1 232.9,512.7 233.6,514.1L298.4,626.3L365.6,742.8C366.3,744.1 367.8,745 369.3,744.9L633.4,744.9C634.9,744.9 636.4,744.1 637.2,742.8L702,630.6C702.7,629.3 704.2,628.4 705.7,628.5L786.3,628.5C789,628.5 791.1,626.3 791.1,623.7L791.1,400.4C791,397.7 788.8,395.5 786.2,395.5ZM477.5,630.6L457.2,665.6C456.9,666.1 456.4,666.6 455.9,666.9C455.3,667.2 454.7,667.4 454,667.4L413.7,667.4C412.3,667.4 411,666.7 410.4,665.4L350.3,561.1L344.4,550.8L322.8,513.9C322.5,513.4 322.3,512.8 322.4,512.1C322.4,511.5 322.6,510.8 322.9,510.3L344.6,472.7L410.5,358.7C411.2,357.5 412.5,356.7 413.8,356.7L454,356.7C454.7,356.7 455.4,356.9 456.1,357.2C456.6,357.5 457.1,357.9 457.4,358.5L477.7,393.7C478.3,394.9 478.2,396.4 477.5,397.5L412.4,510.3C412.1,510.8 412,511.4 412,511.9C412,512.5 412.2,513 412.4,513.5L477.5,626.2C478.4,627.7 478.3,629.3 477.5,630.6ZM679.6,513.9L658,550.8L652.1,561.1L592,665.4C591.3,666.6 590.1,667.4 588.7,667.4L548.4,667.4C547.7,667.4 547.1,667.2 546.5,666.9C546,666.6 545.5,666.2 545.2,665.6L524.9,630.6C524,629.3 524,627.7 524.8,626.4L589.9,513.7C590.2,513.2 590.3,512.6 590.3,512.1C590.3,511.5 590.1,511 589.9,510.5L524.8,397.7C524.1,396.5 524,395.1 524.6,393.9L544.9,358.7C545.2,358.2 545.7,357.7 546.2,357.4C546.8,357 547.5,356.9 548.3,356.9L588.7,356.9C590.1,356.9 591.4,357.6 592,358.9L657.9,472.9L679.6,510.5C679.9,511.1 680.1,511.7 680.1,512.3C680.1,512.7 679.9,513.3 679.6,513.9Z"
/>
</g>
</svg>
)
// PRIVATE API
export const GitHub = () => (
<svg

View File

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

View File

@ -54,6 +54,7 @@ export const LoginSignupForm = ({ state, socialButtonsDirection = 'horizontal',
<SocialAuthButtons gap='large' direction={socialButtonsDirection}>
<SocialButton href={googleSignInUrl}><SocialIcons.Google /></SocialButton>
</SocialAuthButtons>
</SocialAuth>
</>);

View File

@ -1 +1 @@
{"version":3,"file":"LoginSignupForm.jsx","sourceRoot":"","sources":["../../../../../auth/forms/internal/common/LoginSignupForm.tsx"],"names":[],"mappings":"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"}
{"version":3,"file":"LoginSignupForm.jsx","sourceRoot":"","sources":["../../../../../auth/forms/internal/common/LoginSignupForm.tsx"],"names":[],"mappings":"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"}

View File

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

View File

@ -11,6 +11,11 @@ export const Google = () => (<svg className={defaultStyles()} aria-hidden="true"
</g>
</g>
</svg>);
export const Keycloak = () => (<svg className={defaultStyles()} aria-hidden="true" fill="currentColor" viewBox="0 0 559 466">
<g id="brand" transform="matrix(1,0,0,1,-233.075,-279.1)" fill="#000000" fillRule="nonzero">
<path d="M786.2,395.5L705.6,395.5C704.1,395.5 702.6,394.7 701.9,393.4L637.2,281.2C636.4,279.9 635,279.1 633.4,279.1L369.4,279.1C367.9,279.1 366.4,279.9 365.7,281.2L298.4,397.6L233.6,509.8C232.9,511.1 232.9,512.7 233.6,514.1L298.4,626.3L365.6,742.8C366.3,744.1 367.8,745 369.3,744.9L633.4,744.9C634.9,744.9 636.4,744.1 637.2,742.8L702,630.6C702.7,629.3 704.2,628.4 705.7,628.5L786.3,628.5C789,628.5 791.1,626.3 791.1,623.7L791.1,400.4C791,397.7 788.8,395.5 786.2,395.5ZM477.5,630.6L457.2,665.6C456.9,666.1 456.4,666.6 455.9,666.9C455.3,667.2 454.7,667.4 454,667.4L413.7,667.4C412.3,667.4 411,666.7 410.4,665.4L350.3,561.1L344.4,550.8L322.8,513.9C322.5,513.4 322.3,512.8 322.4,512.1C322.4,511.5 322.6,510.8 322.9,510.3L344.6,472.7L410.5,358.7C411.2,357.5 412.5,356.7 413.8,356.7L454,356.7C454.7,356.7 455.4,356.9 456.1,357.2C456.6,357.5 457.1,357.9 457.4,358.5L477.7,393.7C478.3,394.9 478.2,396.4 477.5,397.5L412.4,510.3C412.1,510.8 412,511.4 412,511.9C412,512.5 412.2,513 412.4,513.5L477.5,626.2C478.4,627.7 478.3,629.3 477.5,630.6ZM679.6,513.9L658,550.8L652.1,561.1L592,665.4C591.3,666.6 590.1,667.4 588.7,667.4L548.4,667.4C547.7,667.4 547.1,667.2 546.5,666.9C546,666.6 545.5,666.2 545.2,665.6L524.9,630.6C524,629.3 524,627.7 524.8,626.4L589.9,513.7C590.2,513.2 590.3,512.6 590.3,512.1C590.3,511.5 590.1,511 589.9,510.5L524.8,397.7C524.1,396.5 524,395.1 524.6,393.9L544.9,358.7C545.2,358.2 545.7,357.7 546.2,357.4C546.8,357 547.5,356.9 548.3,356.9L588.7,356.9C590.1,356.9 591.4,357.6 592,358.9L657.9,472.9L679.6,510.5C679.9,511.1 680.1,511.7 680.1,512.3C680.1,512.7 679.9,513.3 679.6,513.9Z"/>
</g>
</svg>);
// PRIVATE API
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"/>

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,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"}

View File

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

View File

@ -1 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../auth/utils.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAMzC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAsCtD,cAAc;AACd,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC,IAAI;KAClB;CACF,CAAA;AAED,cAAc;AACd,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mBAAmB,EAAE,QAAQ;IAC7B,mBAAmB,EAAE,GAAG;CACzB,CAAA;AAiBD,aAAa;AACb,MAAM,UAAU,gBAAgB,CAAC,YAA0B,EAAE,cAAsB;IACjF,OAAO;QACL,YAAY;QACZ,cAAc,EAAE,cAAc,CAAC,WAAW,EAAE;KAC7C,CAAA;AACH,CAAC;AAED,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAsB;IAC3D,OAAO,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;QACpC,KAAK,EAAE;YACL,2BAA2B,EAAE,UAAU;SACxC;KACF,CAAC,CAAC;AACL,CAAC;AAED,aAAa;AACb;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,UAAsB,EACtB,oBAA8C,EAC9C,mBAAsD;IAEtD,8DAA8D;IAC9D,uCAAuC;IACvC,MAAM,4BAA4B,GAAG,MAAM,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;IACrF,MAAM,eAAe,mCAChB,oBAAoB,GACpB,4BAA4B,CAChC,CAAA;IACD,MAAM,sBAAsB,GAAG,MAAM,qBAAqB,CAAK,eAAe,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;QAChC,KAAK,EAAE;YACL,2BAA2B,EAAE,UAAU;SACxC;QACD,IAAI,EAAE,EAAE,YAAY,EAAE,sBAAsB,EAAE;KAC/C,CAAC,CAAC;AACL,CAAC;AAMD,cAAc;AACd,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAA4B;IAE5B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAC,CAAC,CAAC;AAClE,CAAC;AAED,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,UAAsB,EACtB,sBAA+B,EAC/B,UAA+B;IAI/B,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACxB,IAAI,kCAGC,CAAC,UAAU,aAAV,UAAU,cAAV,UAAU,GAAI,EAAS,CAAC,KAC5B,IAAI,EAAE;gBACJ,MAAM,EAAE;oBACN,UAAU,EAAE;wBACR,MAAM,EAAE;4BACJ,YAAY,EAAE,UAAU,CAAC,YAAY;4BACrC,cAAc,EAAE,UAAU,CAAC,cAAc;4BACzC,YAAY,EAAE,sBAAsB;yBACvC;qBACJ;iBACF;aACF,GACF;QACD,mEAAmE;QACnE,kCAAkC;QAClC,OAAO,EAAE;YACP,IAAI,EAAE,IAAI;SACX;KACF,CAAC,CAAA;AACJ,CAAC;AAED,cAAc;AACd,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc;IACrD,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE;gBAC7C,EAAE,EAAE,MAAM;aACX,EAAE,EAAE,CAAC,CAAA;AACR,CAAC;AAED,cAAc;AACd,uDAAuD;AACvD,kDAAkD;AAClD,mDAAmD;AACnD,2BAA2B;AAC3B,gEAAgE;AAChE,2DAA2D;AAC3D,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3D,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC;AAC3B,CAAC;AAED,cAAc;AACd,MAAM,UAAU,wBAAwB,CAAC,CAAU;IACjD,yDAAyD;IACzD,IAAI,CAAC,YAAY,MAAM,CAAC,6BAA6B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE;YACtC,OAAO,EAAE,4CAA4C;SACtD,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,CAAC,YAAY,MAAM,CAAC,2BAA2B,EAAE,CAAC;QACpD,kEAAkE;QAClE,gEAAgE;QAChE,oBAAoB;QACpB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChB,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE;YACtC,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAA;IACJ,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,YAAY,MAAM,CAAC,6BAA6B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5E,qEAAqE;QACrE,kEAAkE;QAClE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChB,OAAO,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAA;QACnF,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE;YACtC,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAA;IACJ,CAAC;IAED,0DAA0D;IAC1D,IAAI,CAAC,YAAY,MAAM,CAAC,6BAA6B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5E,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChB,OAAO,CAAC,IAAI,CAAC;;uGAEsF,CAAC,CAAA;QACpG,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,aAAa,EAAE;YACtC,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,CAAC,CAAA;AACT,CAAC;AAED,cAAc;AACd,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAEC,EACD,gBAAmC;IAEnC,MAAM,EACJ,QAAQ,EAAE,SAAS,KAEjB,IAAI,EADH,aAAa,UACd,IAAI,EAHF,YAGL,CAAO,CAAC;IACT,MAAM,MAAM,GAAwB,EAAE,CAAC;IAEvC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAA;YAChD,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAA;QACvB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,aAAa;AACb,MAAM,UAAU,kCAAkC,CAChD,YAAoB,EACpB,EAAE,yBAAyB,GAAG,KAAK,KAA8C,EAAE;IAEnF,wFAAwF;IACxF,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAA6B,CAAC;IAEhE,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,yBAAyB,EAAE,CAAC;QACpE,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,YAAsC;IAEtC,OAAO,qBAAqB,CAC1B,MAAM,oBAAoB,CAAC,YAAY,CAAC,CACzC,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAA0B,YAAsC;IAC5F,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,YAAsC;IAEtC,MAAM,IAAI,qBACL,YAAY,CAChB,CAAC;IACF,IAAI,4BAA4B,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,cAAc,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAGD,SAAS,4BAA4B,CACnC,YAA8D;IAE9D,OAAO,gBAAgB,IAAI,YAAY,CAAC;AAC1C,CAAC;AAED,cAAc;AACd,MAAM,UAAU,4BAA4B,CAAC,OAAgB;IAC3D,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,qBAAqB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;AAC9D,CAAC"}
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../auth/utils.ts"],"names":[],"mappings":"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"}

View File

@ -22,6 +22,7 @@ app todoApp {
configFn: import { config } from "@src/auth/github.js",
userSignupFields: import { userSignupFields } from "@src/auth/github.js"
},
keycloak: {},
email: {
userSignupFields: import { userSignupFields } from "@src/auth/email.js",
fromField: {

View File

@ -21,6 +21,11 @@ export function getName(user?: User) {
return `GitHub user ${githubIdentity.providerUserId}`
}
const keycloakIdentity = findUserIdentity(user, 'keycloak')
if (keycloakIdentity) {
return `Keycloak user ${keycloakIdentity.providerUserId}`
}
// If we don't know how to get the name, return null.
return null
}

View File

@ -10,6 +10,7 @@ module Wasp.AppSpec.App.Auth
isUsernameAndPasswordAuthEnabled,
isExternalAuthEnabled,
isGoogleAuthEnabled,
isKeycloakAuthEnabled,
isGitHubAuthEnabled,
isEmailAuthEnabled,
userSignupFieldsForEmailAuth,
@ -40,6 +41,7 @@ data AuthMethods = AuthMethods
{ usernameAndPassword :: Maybe UsernameAndPasswordConfig,
google :: Maybe ExternalAuthConfig,
gitHub :: Maybe ExternalAuthConfig,
keycloak :: Maybe ExternalAuthConfig,
email :: Maybe EmailAuthConfig
}
deriving (Show, Eq, Data)
@ -67,11 +69,21 @@ isUsernameAndPasswordAuthEnabled :: Auth -> Bool
isUsernameAndPasswordAuthEnabled = isJust . usernameAndPassword . methods
isExternalAuthEnabled :: Auth -> Bool
isExternalAuthEnabled auth = any ($ auth) [isGoogleAuthEnabled, isGitHubAuthEnabled]
isExternalAuthEnabled auth =
any
($ auth)
-- NOTE: Make sure to add new external auth methods here.
[ isGoogleAuthEnabled,
isGitHubAuthEnabled,
isKeycloakAuthEnabled
]
isGoogleAuthEnabled :: Auth -> Bool
isGoogleAuthEnabled = isJust . google . methods
isKeycloakAuthEnabled :: Auth -> Bool
isKeycloakAuthEnabled = isJust . keycloak . methods
isGitHubAuthEnabled :: Auth -> Bool
isGitHubAuthEnabled = isJust . gitHub . methods

View File

@ -14,6 +14,14 @@ googleAuthProvider =
OA._requiredScope = ["profile"]
}
keycloakAuthProvider :: OA.OAuthAuthProvider
keycloakAuthProvider =
OA.OAuthAuthProvider
{ OA._providerId = fromJust $ makeProviderId "keycloak",
OA._displayName = "Keycloak",
OA._requiredScope = ["profile"]
}
gitHubAuthProvider :: OA.OAuthAuthProvider
gitHubAuthProvider =
OA.OAuthAuthProvider

View File

@ -6,7 +6,11 @@ where
import Data.Aeson (object, (.=))
import StrongPath (reldir, relfile, (</>))
import qualified Wasp.AppSpec.App.Auth as AS.Auth
import Wasp.Generator.AuthProviders (gitHubAuthProvider, googleAuthProvider)
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
import qualified Wasp.Generator.AuthProviders.OAuth as OAuth
import Wasp.Generator.FileDraft (FileDraft)
import Wasp.Generator.Monad (Generator)
@ -114,6 +118,9 @@ genLoginSignupForm auth =
-- Google
"isGoogleAuthEnabled" .= AS.Auth.isGoogleAuthEnabled auth,
"googleSignInPath" .= OAuth.serverLoginUrl googleAuthProvider,
-- Keycloak
"isKeycloakAuthEnabled" .= AS.Auth.isKeycloakAuthEnabled auth,
"keycloakSignInPath" .= OAuth.serverLoginUrl keycloakAuthProvider,
-- GitHub
"isGitHubAuthEnabled" .= AS.Auth.isGitHubAuthEnabled auth,
"gitHubSignInPath" .= OAuth.serverLoginUrl gitHubAuthProvider,

View File

@ -7,7 +7,11 @@ import Data.Aeson (object, (.=))
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, googleAuthProvider)
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
import Wasp.Generator.AuthProviders.OAuth (OAuthAuthProvider)
import qualified Wasp.Generator.AuthProviders.OAuth as OAuth
import Wasp.Generator.FileDraft (FileDraft)
@ -25,11 +29,13 @@ genHelpers auth =
return $
concat
[ [gitHubHelpers | AS.Auth.isGitHubAuthEnabled auth],
[googleHelpers | AS.Auth.isGoogleAuthEnabled auth]
[googleHelpers | AS.Auth.isGoogleAuthEnabled auth],
[keycloakHelpers | AS.Auth.isKeycloakAuthEnabled auth]
]
where
gitHubHelpers = mkHelpersFd gitHubAuthProvider [relfile|GitHub.tsx|]
googleHelpers = mkHelpersFd googleAuthProvider [relfile|Google.tsx|]
keycloakHelpers = mkHelpersFd keycloakAuthProvider [relfile|Keycloak.tsx|]
mkHelpersFd :: OAuthAuthProvider -> Path' Rel' File' -> FileDraft
mkHelpersFd provider helpersFp =

View File

@ -28,6 +28,7 @@ genNewClientAuth spec =
<++> genAuthEmail auth
<++> genAuthUsername auth
<++> genAuthGoogle auth
<++> genAuthKeycloak auth
<++> genAuthGitHub auth
where
maybeAuth = AS.App.auth $ snd $ getApp spec
@ -68,6 +69,12 @@ genAuthGoogle auth =
then sequence [genFileCopy [relfile|client/auth/google.ts|]]
else return []
genAuthKeycloak :: AS.Auth.Auth -> Generator [FileDraft]
genAuthKeycloak auth =
if AS.Auth.isKeycloakAuthEnabled auth
then sequence [genFileCopy [relfile|client/auth/keycloak.ts|]]
else return []
genAuthGitHub :: AS.Auth.Auth -> Generator [FileDraft]
genAuthGitHub auth =
if AS.Auth.isGitHubAuthEnabled auth

View File

@ -26,7 +26,11 @@ import qualified Wasp.AppSpec.App.Auth as AS.App.Auth
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, googleAuthProvider)
import Wasp.Generator.AuthProviders
( gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
)
import Wasp.Generator.AuthProviders.OAuth
( OAuthAuthProvider,
clientOAuthCallbackPath,
@ -49,6 +53,7 @@ genOAuthAuth auth
| AS.Auth.isExternalAuthEnabled auth =
genOAuthHelpers 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)
| otherwise = return []

View File

@ -20,7 +20,13 @@ import qualified Wasp.AppSpec.App as AS.App
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, gitHubAuthProvider, googleAuthProvider, localAuthProvider)
import Wasp.Generator.AuthProviders
( emailAuthProvider,
gitHubAuthProvider,
googleAuthProvider,
keycloakAuthProvider,
localAuthProvider,
)
import qualified Wasp.Generator.AuthProviders.Email as EmailProvider
import qualified Wasp.Generator.AuthProviders.Local as LocalProvider
import qualified Wasp.Generator.AuthProviders.OAuth as OAuthProvider
@ -72,6 +78,7 @@ genProvidersIndex auth = return $ C.mkTmplFdWithData [relfile|src/auth/providers
<$> concat
[ [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],
[EmailProvider.providerId emailAuthProvider | AS.Auth.isEmailAuthEnabled auth]
]

View File

@ -9,7 +9,6 @@ import Data.List (intercalate)
import Data.Maybe (fromJust)
import qualified StrongPath as SP
import Test.Tasty.Hspec
import qualified Wasp.AI.GenerateNewProject.Common as Auth
import Wasp.Analyzer
import Wasp.Analyzer.Parser (Ctx)
import qualified Wasp.Analyzer.TypeChecker as TC
@ -145,6 +144,7 @@ spec_Analyzer = do
{ Auth.userSignupFields = Just $ ExtImport (ExtImportField "getUserFields") (fromJust $ SP.parseRelFileP "auth/signup.js")
},
Auth.google = Nothing,
Auth.keycloak = Nothing,
Auth.gitHub = Nothing,
Auth.email = Nothing
},

View File

@ -104,6 +104,7 @@ spec_AppSpecValid = do
{ AS.Auth.usernameAndPassword = Just AS.Auth.UsernameAndPasswordConfig {AS.Auth.userSignupFields = Nothing},
AS.Auth.google = Nothing,
AS.Auth.gitHub = Nothing,
AS.Auth.keycloak = Nothing,
AS.Auth.email = Nothing
},
AS.Auth.onAuthFailedRedirectTo = "/",
@ -185,7 +186,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, gitHub = Nothing, email = Nothing}) validUserEntity) `shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = 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
@ -197,6 +198,7 @@ spec_AppSpecValid = do
{ AS.Auth.userSignupFields = Nothing
},
google = Nothing,
keycloak = Nothing,
gitHub = Nothing,
email = Nothing
}
@ -204,7 +206,7 @@ spec_AppSpecValid = do
validUserEntity
)
`shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = Nothing, google = Nothing, gitHub = Nothing, email = Just emailAuthConfig}) validUserEntity) `shouldBe` []
ASV.validateAppSpec (makeSpec (AS.Auth.AuthMethods {usernameAndPassword = 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
@ -216,6 +218,7 @@ spec_AppSpecValid = do
{ AS.Auth.userSignupFields = Nothing
},
google = Nothing,
keycloak = Nothing,
gitHub = Nothing,
email = Just emailAuthConfig
}
@ -281,7 +284,7 @@ spec_AppSpecValid = do
Just
AS.Auth.Auth
{ AS.Auth.methods =
AS.Auth.AuthMethods {email = Just emailAuthConfig, usernameAndPassword = Nothing, google = Nothing, gitHub = Nothing},
AS.Auth.AuthMethods {email = Just emailAuthConfig, usernameAndPassword = Nothing, google = Nothing, keycloak = Nothing, gitHub = Nothing},
AS.Auth.userEntity = AS.Core.Ref.Ref userEntityName,
AS.Auth.externalAuthEntity = Nothing,
AS.Auth.onAuthFailedRedirectTo = "/",

View File

@ -3,6 +3,7 @@
--auth-pills-email: #e0f2fe;
--auth-pills-github: #f1f5f9;
--auth-pills-google: #ecfccb;
--auth-pills-keycloak: #d0ebf5;
--auth-pills-username-and-pass: #fce7f3;
}
@ -11,5 +12,6 @@
--auth-pills-email: #0c4a6e;
--auth-pills-github: #334155;
--auth-pills-google: #365314;
--auth-pills-keycloak: #2d5866;
--auth-pills-username-and-pass: #831843;
}

View File

@ -20,14 +20,6 @@ export function Pill({ children, linkToPage, style = {} }) {
)
}
/*
:root {
--auth-pills-email: #e0f2fe;
--auth-pills-github: #f1f5f9;
--auth-pills-google: #ecfccb;
--auth-pills-username-and-pass: #fce7f3;
}
*/
export function EmailPill() {
return (
<Pill
@ -79,3 +71,16 @@ export function GooglePill() {
</Pill>
)
}
export function KeycloakPill() {
return (
<Pill
style={{
backgroundColor: 'var(--auth-pills-keycloak)',
}}
linkToPage="/docs/auth/social-auth/keycloak"
>
Keycloak
</Pill>
)
}

View File

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

View File

@ -0,0 +1,547 @@
---
title: Keycloak
---
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 Keycloak Authentication out of the box.
[Keycloak](https://www.keycloak.org/) is an open-source identity and access management solution for modern applications and services. Keycloak provides both SAML and OpenID protocol solutions. It also has a very flexible and powerful administration UI.
Let's walk through enabling Keycloak authentication, explain some of the default settings, and show how to override them.
## Setting up Keycloak Auth
Enabling Keycloak Authentication comes down to a series of steps:
1. Enabling Keycloak authentication in the Wasp file.
1. Adding the `User` entity.
1. Creating a Keycloak client.
1. Adding the necessary Routes and Pages
1. Using Auth UI components in our Pages.
<WaspFileStructureNote />
### 1. Adding Keycloak 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.13.0"
},
title: "My App",
auth: {
// 1. Specify the User entity (we'll define it next)
// highlight-next-line
userEntity: User,
methods: {
// 2. Enable Keycloak Auth
// highlight-next-line
keycloak: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.13.0"
},
title: "My App",
auth: {
// 1. Specify the User entity (we'll define it next)
// highlight-next-line
userEntity: User,
methods: {
// 2. Enable Keycloak Auth
// highlight-next-line
keycloak: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
The `userEntity` is explained in [the social auth overview](../../auth/social-auth/overview#social-login-entity).
### 2. Adding 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 Keycloak Client
1. Log into your Keycloak admin console.
1. Under **Clients**, click on **Create Client**.
![Keycloak Screenshot 1](/img/auth/keycloak/1-keycloak.png)
1. Fill in the **Client ID** and choose a name for the client.
![Keycloak Screenshot 2](/img/auth/keycloak/2-keycloak.png)
1. In the next step, enable **Client Authentication**.
![Keycloak Screenshot 3](/img/auth/keycloak/3-keycloak.png)
1. Under **Valid Redirect URIs**, add `http://localhost:3001/auth/keycloak/callback` for local development.
![Keycloak Screenshot 4](/img/auth/keycloak/4-keycloak.png)
- Once you know on which URL(s) your API server will be deployed, also add those URL(s).
- For example: `https://my-server-url.com/auth/keycloak/callback`.
1. Click **Save**.
1. In the **Credentials** tab, copy the **Client Secret** value, which we'll use in the next step.
![Keycloak Screenshot 5](/img/auth/keycloak/5-keycloak.png)
### 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"
KEYCLOAK_CLIENT_ID=your-keycloak-client-id
KEYCLOAK_CLIENT_SECRET=your-keycloak-client-secret
KEYCLOAK_REALM_URL=https://your-keycloak-url.com/realms/master
```
We assumed in the `KEYCLOAK_REALM_URL` env variable that you are using the `master` realm. If you are using a different realm, replace `master` with your realm name.
### 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. Create 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 now create an `auth.{jsx,tsx}` file in the `src/pages`.
It should have the following code:
<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>
:::info Auth UI
Our pages use an automatically generated Auth UI component. Read more about Auth UI components [here](../../auth/ui).
:::
### Conclusion
Yay, we've successfully set up Keycloak Auth!
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 `keycloak: {}` 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.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
// highlight-next-line
keycloak: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title=main.wasp
app myApp {
wasp: {
version: "^0.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
// highlight-next-line
keycloak: {}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
<DefaultBehaviour />
## Overrides
<OverrideIntro />
### Data Received From Keycloak
We are using Keycloak's API and its `/userinfo` endpoint to fetch the user's data.
```ts title="Keycloak user data"
{
sub: '5adba8fc-3ea6-445a-a379-13f0bb0b6969',
email_verified: true,
name: 'Test User',
preferred_username: 'test',
given_name: 'Test',
family_name: 'User',
email: 'test@example.com'
}
```
The fields you receive will depend on the scopes you requested. The default scope is set to `profile` only. If you want to get the user's email, you need to specify the `email` scope in the `configFn` function.
<small>
For up-to-date info about the data received from Keycloak, please refer to the [Keycloak API documentation](https://www.keycloak.org/docs-api/23.0.7/javadocs/org/keycloak/representations/UserInfo.html).
</small>
### Using the Data Received From Keycloak
<OverrideExampleIntro />
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
keycloak: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/keycloak.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/keycloak.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
entity User {=psl
id Int @id @default(autoincrement())
username String @unique
displayName String
psl=}
// ...
```
```js title=src/auth/keycloak.js
export const userSignupFields = {
username: () => "hardcoded-username",
displayName: (data) => data.profile.name,
}
export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
keycloak: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/keycloak.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/keycloak.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
entity User {=psl
id Int @id @default(autoincrement())
username String @unique
displayName String
psl=}
// ...
```
```ts title=src/auth/keycloak.ts
import { defineUserSignupFields } from 'wasp/server/auth'
export const userSignupFields = defineUserSignupFields({
username: () => "hardcoded-username",
displayName: (data: any) => data.profile.name,
})
export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```
<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.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
keycloak: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/keycloak.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/keycloak.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "^0.13.0"
},
title: "My App",
auth: {
userEntity: User,
methods: {
keycloak: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/keycloak.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/keycloak.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```
</TabItem>
</Tabs>
The `keycloak` dict has the following properties:
- #### `configFn: ExtImport`
This function must return an object with the scopes for the OAuth provider.
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```js title=src/auth/keycloak.js
export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```ts title=src/auth/keycloak.ts
export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```
</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 } from "./Pills";
import { EmailPill, UsernameAndPasswordPill, GithubPill, GooglePill, KeycloakPill } 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 /> and <GooglePill /> authentication.
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill /> and <KeycloakPill /> 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 /> and <GooglePill /> authentication.
Used with <UsernameAndPasswordPill />, <EmailPill />, <GithubPill />, <GooglePill /> and <KeycloakPill /> authentication.
![Signup form](/img/authui/signup.png)

View File

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

View File

@ -23,6 +23,11 @@ export function AuthMethodsGrid() {
description: 'Users sign in with their Github account',
linkToDocs: '/docs/auth/social-auth/github',
},
{
title: 'Keycloak',
description: 'Users sign in with their Keycloak account',
linkToDocs: '/docs/auth/social-auth/keycloak',
}
]
return (
<>

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB