Skip to content

Commit

Permalink
🕷️
Browse files Browse the repository at this point in the history
  • Loading branch information
quochuydev committed Aug 15, 2024
1 parent 8722569 commit d2d4553
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 50 deletions.
3 changes: 0 additions & 3 deletions app1/ui/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ export default function Home() {
onSelectAccount={(session) => console.log(session)}
session={sessions[0]}
sessions={sessions}
signOut={() => {
window.location.href = `https://auth.example.local/auth/signout?id_token_hint=${sessions[0]?.idToken}&return_url=https://app.example.local/app1`;
}}
/>
</div>
</nav>
Expand Down
19 changes: 13 additions & 6 deletions app1/ui/components/ProfileImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ export default function ProfileImage(props: {
session: any;
sessions: any[];
onSelectAccount: (session: any) => void;
signOut: (sessionId: string) => void;
}) {
const { session, sessions, onSelectAccount, signOut } = props;
const { session, sessions } = props;
const router = useRouter();

const user = {
Expand Down Expand Up @@ -50,7 +49,6 @@ export default function ProfileImage(props: {
{() => (
<button
onClick={() => {
// onSelectAccount(session);
router.push(`/account/${index}`);
}}
className={`group flex items-center w-full px-2 py-2 text-sm`}
Expand All @@ -76,7 +74,6 @@ export default function ProfileImage(props: {
className={`group flex justify-center items-center w-full px-2 py-2 text-sm`}
onClick={() => {
const params = new URLSearchParams({
// login_hint: 'username',
prompt: "select_account",
scope: [
"openid",
Expand All @@ -91,7 +88,7 @@ export default function ProfileImage(props: {
return_url: "https://app.example.local/app1",
});

window.location.href = `https://auth.example.local/auth/signin?${params}`;
window.location.href = `https://auth.example.local/auth/signin/portal?${params}`;
}}
>
+ Add other account
Expand All @@ -103,7 +100,17 @@ export default function ProfileImage(props: {
{() => (
<button
className={`group flex justify-center items-center w-full px-2 py-2 text-sm`}
onClick={() => signOut(session.id)}
onClick={() => {
const params = new URLSearchParams({
return_url: "https://app.example.local/app1",
});

if (session?.idToken) {
params.set("id_token_hint", session.idToken);
}

window.location.href = `https://auth.example.local/auth/signout/portal?${params}`;
}}
>
Logout
</button>
Expand Down
10 changes: 7 additions & 3 deletions app2/ui/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useEffect, useState } from "react";

export default function Home() {
const [sessions, setSessions] = useState([]);
const [sessions, setSessions] = useState<any[]>([]);

useEffect(() => {
reloadSessions();
Expand Down Expand Up @@ -37,7 +37,7 @@ export default function Home() {
return_url: "https://app.example.local/app2",
});

window.location.href = `https://auth.example.local/auth/signin?${params}`;
window.location.href = `https://auth.example.local/auth/signin/zitadel?${params}`;
}}
>
Login
Expand All @@ -48,7 +48,11 @@ export default function Home() {
return_url: "https://app.example.local/app2",
});

window.location.href = `https://auth.example.local/auth/signout?${params}`;
if (sessions[0]?.idToken) {
params.set("id_token_hint", sessions[0].idToken);
}

window.location.href = `https://auth.example.local/auth/signout/zitadel?${params}`;
}}
>
Logout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { NextRequest, NextResponse } from "next/server";
import { URLSearchParams } from "url";
import { v4 as uuid } from "uuid";

async function handler(request: NextRequest) {
async function handler(
request: NextRequest,
{ params }: { params: { provider: "portal" | "zitadel" } }
) {
try {
const code = request.nextUrl.searchParams.get("code");
const state = request.nextUrl.searchParams.get("state");
Expand All @@ -36,15 +39,18 @@ async function handler(request: NextRequest) {
if (redirectCookie.value !== configuration.redirectUrl)
throw new Error("Invalid redirect url");

const provider = params.provider;
if (!provider) throw new Error("provider not found");

const tokenParams = new URLSearchParams();
tokenParams.append("code", code as string);
tokenParams.append("grant_type", "authorization_code");
tokenParams.append("client_id", configuration.portal.clientId);
tokenParams.append("client_id", configuration[provider].clientId);
tokenParams.append("redirect_uri", configuration.redirectUrl);
tokenParams.append("code_verifier", codeVerifierCookie.value);

const wellKnownResponse = await fetch(
`${configuration.portal.issuer}/.well-known/openid-configuration`
`${configuration[provider].issuer}/.well-known/openid-configuration`
);

const wellKnown = (await wellKnownResponse.json()) as {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { URLSearchParams } from "url";

export async function POST(request: NextRequest) {
export async function POST(
request: NextRequest,
{ params }: { params: { provider: "portal" | "zitadel" } }
) {
const body = (await request.json()) as {
csrfToken: string;
scope: string;
Expand All @@ -26,14 +29,17 @@ export async function POST(request: NextRequest) {
};
const { csrfToken, scope, returnUrl, prompt, loginHint } = body;

const provider = params.provider;
if (!provider) throw new Error("provider not found");

const requestCookie = cookies();

const csrfTokenCookie = requestCookie.get(csrfTokenCookieName);
if (!csrfTokenCookie) throw new Error("csrfToken cookie not found");
if (csrfTokenCookie.value !== csrfToken) throw new Error("Invalid csrfToken");

const wellKnownResponse = await fetch(
`${configuration.portal.issuer}/.well-known/openid-configuration`
`${configuration[provider].issuer}/.well-known/openid-configuration`
);

const wellKnown = (await wellKnownResponse.json()) as {
Expand All @@ -52,18 +58,18 @@ export async function POST(request: NextRequest) {
const codeChallenge = generateCodeChallenge(codeVerifier);
const state = generateState();

const params = new URLSearchParams({
const requestParams = new URLSearchParams({
code_challenge: codeChallenge,
code_challenge_method: "S256",
client_id: configuration.portal.clientId,
client_id: configuration[provider].clientId,
redirect_uri: configuration.redirectUrl,
response_type: "code",
scope,
state,
});

if (prompt) params.set("prompt", prompt);
if (loginHint) params.set("login_hint", loginHint);
if (prompt) requestParams.set("prompt", prompt);
if (loginHint) requestParams.set("login_hint", loginHint);

if (returnUrl) setShortLiveCookie(returnUrlCookieName, returnUrl);
setShortLiveCookie(stateCookieName, state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { Prisma } from "@prisma/client";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
export async function POST(
request: NextRequest,
{ params }: { params: { provider: "portal" | "zitadel" } }
) {
try {
const body = (await request.json()) as {
returnUrl?: string;
Expand All @@ -17,12 +20,11 @@ export async function POST(request: NextRequest) {
};
const { returnUrl, idTokenHint, state } = body;

const requestCookie = cookies();
const authSessionCookie = requestCookie.get(authSessionCookieName);
const authSession = authSessionCookie?.value;
const provider = params.provider;
if (!provider) throw new Error("provider not found");

const wellKnownResponse = await fetch(
`${configuration.portal.issuer}/.well-known/openid-configuration`
`${configuration[provider].issuer}/.well-known/openid-configuration`
);

const wellKnown = (await wellKnownResponse.json()) as {
Expand All @@ -37,28 +39,33 @@ export async function POST(request: NextRequest) {
throw { code: wellKnownResponse.status, details: wellKnown };
}

const params = new URLSearchParams({
client_id: configuration.portal.clientId,
const requestParams = new URLSearchParams({
client_id: configuration[provider].clientId,
post_logout_redirect_uri: configuration.postLogoutRedirectUri,
});

if (state) params.set("state", state);
if (state) requestParams.set("state", state);
if (idTokenHint) requestParams.set("id_token_hint", idTokenHint);

const sessionWhereInput: Prisma.SessionWhereInput = {
authSession: authSessionCookie?.value,
};
const requestCookie = cookies();
const authSessionCookie = requestCookie.get(authSessionCookieName);

if (idTokenHint) {
params.set("id_token_hint", idTokenHint);
sessionWhereInput.idToken = idTokenHint;
}
if (authSessionCookie?.value) {
const sessionWhereInput: Prisma.SessionWhereInput = {
authSession: authSessionCookie.value,
};

await prisma.session.updateMany({
where: sessionWhereInput,
data: {
deletedAt: new Date(),
},
});
if (idTokenHint) {
sessionWhereInput.idToken = idTokenHint;
}

await prisma.session.updateMany({
where: sessionWhereInput,
data: {
deletedAt: new Date(),
},
});
}

if (returnUrl) setShortLiveCookie(returnUrlCookieName, returnUrl);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import SignIn from "@/ui/SignIn";

export default async function Page({
searchParams,
params,
}: {
searchParams: {
return_url?: string;
prompt?: string;
scope?: string;
login_hint?: string;
};
params: { provider: string };
}) {
return (
<SignIn
provider={params.provider}
returnUrl={searchParams.return_url}
prompt={searchParams.prompt}
scope={searchParams.scope}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SignOut from "@/ui/SignOut";

export default async function Page({
searchParams,
params,
}: {
searchParams: {
return_url?: string;
Expand All @@ -10,9 +11,12 @@ export default async function Page({
post_logoutRedirect_uri?: string;
state?: string;
};
params: { provider: string };
}) {
console.log(`debug:params`, params);
return (
<SignOut
provider={params.provider}
returnUrl={searchParams.return_url}
idTokenHint={searchParams.id_token_hint}
clientId={searchParams.client_id}
Expand Down
7 changes: 6 additions & 1 deletion auth/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ const schema = z.object({
portal: z.object({
clientId: z.string(),
issuer: z.string(),
redirectUrl: z.string(),
}),
zitadel: z.object({
clientId: z.string(),
issuer: z.string(),
}),
redirectUrl: z.string(),
postLogoutRedirectUri: z.string(),
});

const configuration = {
Expand Down
Binary file modified auth/prisma/dev.db
Binary file not shown.
7 changes: 4 additions & 3 deletions auth/ui/SignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { useEffect } from "react";
import { useRouter } from "next/navigation";

export default function SignIn(props: {
provider: string;
returnUrl?: string;
prompt?: string;
scope?: string;
loginHint?: string;
}) {
const router = useRouter();
const { returnUrl, prompt, scope, loginHint } = props;
const { provider, returnUrl, prompt, scope, loginHint } = props;

useEffect(() => {
fetch(`https://auth.example.local/api/auth/csrf`, {
Expand All @@ -19,7 +20,7 @@ export default function SignIn(props: {
.then(async ({ csrfToken }) => {
if (csrfToken) {
const result = await fetch(
`https://auth.example.local/api/auth/signin`,
`https://auth.example.local/api/auth/signin/${provider}`,
{
method: "POST",
body: JSON.stringify({
Expand All @@ -35,7 +36,7 @@ export default function SignIn(props: {
if (result.authorizeUrl) router.replace(result.authorizeUrl);
}
});
}, [loginHint, prompt, returnUrl, router, scope]);
}, [loginHint, prompt, provider, returnUrl, router, scope]);

return <div>Loading...</div>;
}
23 changes: 19 additions & 4 deletions auth/ui/SignOut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ import { useEffect } from "react";
import { useRouter } from "next/navigation";

export default function SignOut(props: {
provider: string;
returnUrl?: string;
idTokenHint?: string;
clientId?: string;
postLogoutRedirectUri?: string;
state?: string;
}) {
const { returnUrl, idTokenHint, clientId, postLogoutRedirectUri, state } =
props;
const {
provider,
returnUrl,
idTokenHint,
clientId,
postLogoutRedirectUri,
state,
} = props;
const router = useRouter();

useEffect(() => {
fetch(`https://auth.example.local/api/auth/signout`, {
fetch(`https://auth.example.local/api/auth/signout/${provider}`, {
method: "POST",
body: JSON.stringify({
returnUrl,
Expand All @@ -28,7 +35,15 @@ export default function SignOut(props: {
.then(({ endSessionUrl }) => {
if (endSessionUrl) router.replace(endSessionUrl);
});
}, [clientId, idTokenHint, postLogoutRedirectUri, returnUrl, router, state]);
}, [
clientId,
idTokenHint,
postLogoutRedirectUri,
provider,
returnUrl,
router,
state,
]);

return <div>Loading...</div>;
}

0 comments on commit d2d4553

Please sign in to comment.