Skip to content

Commit

Permalink
Merge pull request #323 from panoratech/feat/b2c-auth
Browse files Browse the repository at this point in the history
🐛 Fix auth b2c
  • Loading branch information
naelob authored Mar 18, 2024
2 parents 0f12bff + bab6b0d commit 55c7014
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 204 deletions.
6 changes: 4 additions & 2 deletions apps/client-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@stytch/nextjs": "^18.0.0",
"@stytch/vanilla-js": "^4.7.1",
"@tanstack/react-query": "^5.12.2",
"@tanstack/react-query-devtools": "^5.25.0",
"@tanstack/react-query-next-experimental": "^5.25.0",
"@tanstack/react-table": "^8.11.8",
"antd": "^5.11.5",
"api": "workspace:*",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"cmdk": "^0.2.1",
Expand All @@ -50,8 +53,7 @@
"tailwind-merge": "^2.2.1",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.22.4",
"zustand": "^4.4.7",
"api": "workspace:*"
"zustand": "^4.4.7"
},
"devDependencies": {
"@types/cookies": "^0.9.0",
Expand Down
63 changes: 63 additions & 0 deletions apps/client-ts/src/app/authenticate/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"use client";

import { Suspense, useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { useStytchUser, useStytch } from "@stytch/nextjs";

const OAUTH_TOKEN = "oauth";
const MAGIC_LINKS_TOKEN = "magic_links";

/**
* During both the Magic link and OAuth flows, Stytch will redirect the user back to your application to a specified redirect URL (see Login.tsx).
* Stytch will append query parameters to the redirect URL which are then used to complete the authentication flow.
* A redirect URL for this example app will look something like: http://localhost:3000/authenticate?stytch_token_type=magic_links&token=abc123
*
* The AuthenticatePage will detect the presence of a token in the query parameters, and attempt to authenticate it.
*
* On successful authentication, a session will be created and the user will be redirect to /profile.
*/
const InnerAuthenticate = () => {
const { user, isInitialized } = useStytchUser();
const stytch = useStytch();
const router = useRouter();
const searchParams = useSearchParams();

useEffect(() => {
if (stytch && !user && isInitialized) {
const token = searchParams.get("token");
const stytch_token_type = searchParams.get("stytch_token_type");

if (token && stytch_token_type === OAUTH_TOKEN) {
stytch.oauth.authenticate(token, {
session_duration_minutes: 60,
});
} else if (token && stytch_token_type === MAGIC_LINKS_TOKEN) {
stytch.magicLinks.authenticate(token, {
session_duration_minutes: 60,
});
}
}
}, [isInitialized, router, searchParams, stytch, user]);

useEffect(() => {
if (!isInitialized) {
return;
}
if (user) {
router.replace("/b2c/profile");
}
}, [router, user, isInitialized]);

return null;
};

const Authenticate = () => {

return (
<Suspense>
<InnerAuthenticate/>
</Suspense>
)
}

export default Authenticate;
17 changes: 17 additions & 0 deletions apps/client-ts/src/app/b2c/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import LoginWithStytchSDKUI from "@/components/Auth/b2c/LoginWithStytchSDKUI";

export default async function Page() {
return (
<div className='min-h-screen grid lg:grid-cols-2 mx-auto text-left'>
<div className='flex-1 flex flex-col justify-center py-12 px-4 sm:px-6 lg:flex-none lg:px-20 xl:px-24'>
<img src="/logo.png" className='w-14' />
<LoginWithStytchSDKUI />
</div>
<div className='hidden lg:block relative flex-1'>
<img className='absolute inset-0 h-full w-full object-cover border-l' src="/bgbg.jpeg" alt='Login Page Image' />
</div>
</div>
)
}
16 changes: 16 additions & 0 deletions apps/client-ts/src/app/b2c/profile/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "./../../globals.css";
import { RootLayout } from "@/components/RootLayout";


export default function Layout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<>
<RootLayout/>
{children}
</>
);
}
60 changes: 60 additions & 0 deletions apps/client-ts/src/app/b2c/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client'

import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input";
import { Separator } from "@/components/ui/separator"
import { useRouter } from "next/navigation";
import { useStytch, useStytchSession, useStytchUser } from "@stytch/nextjs";

const Profile = () => {
const stytch = useStytch();
// Get the Stytch User object if available
const { user } = useStytchUser();
// Get the Stytch Session object if available
const { session } = useStytchSession();
const router = useRouter();

return (
<div className="ml-[200px] p-10">
<Card>
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>
<div className="flex items-center">
{user?.name.first_name} {user?.name.last_name}
</div>
</CardDescription>
</CardHeader>
<CardContent>
<h4 className="text-sm font-medium mb-2">Connected user</h4>

<div className="flex space-x-2">
<Input value={`${user?.emails[0].email}`} readOnly />
<Button variant="secondary" className="shrink-0">
Copy
</Button>
</div>
<Separator className="my-4" />
<div className="pt-4">
<Button onClick={() => {
stytch.session.revoke()
router.push('/b2c/login');
}}>
Log Out
</Button>
</div>
</CardContent>
</Card>
</div>
);
};


export default Profile;
51 changes: 51 additions & 0 deletions apps/client-ts/src/components/Auth/b2c/LoginWithStytchSDKUI.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use client';

import { StytchLogin } from '@stytch/nextjs';
import { StytchLoginConfig, OAuthProviders, Products, StyleConfig, StytchEvent, StytchError } from '@stytch/vanilla-js';
import { getDomainFromWindow } from '@/lib/stytch/urlUtils';

const sdkStyle: StyleConfig = {
fontFamily: '"Helvetica New", Helvetica, sans-serif',
buttons: {
primary: {
backgroundColor: '#19303d',
textColor: '#ffffff',
},
},
};

const sdkConfig: StytchLoginConfig = {
products: [Products.oauth, Products.emailMagicLinks],
emailMagicLinksOptions: {
loginRedirectURL: getDomainFromWindow() + '/authenticate',
loginExpirationMinutes: 30,
signupRedirectURL: getDomainFromWindow() + '/authenticate',
signupExpirationMinutes: 30,
createUserAsPending: false,
},
oauthOptions: {
providers: [
{ type: OAuthProviders.Google },
{ type: OAuthProviders.Apple },
{ type: OAuthProviders.Microsoft },
{ type: OAuthProviders.Facebook },
{ type: OAuthProviders.Github },
{ type: OAuthProviders.GitLab },
],
loginRedirectURL: getDomainFromWindow() + '/authenticate',
signupRedirectURL: getDomainFromWindow() + '/authenticate',
},
};

const callbackConfig = {
onEvent: (message: StytchEvent) => console.log(message),
onError: (error: StytchError) => console.log(error),
}

const LoginWithStytchSDKUI = () => {
return (
<StytchLogin config={sdkConfig} styles={sdkStyle} callbacks={callbackConfig} />
)
}

export default LoginWithStytchSDKUI;
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const AddLinkedAccount = () => {

const posthog = usePostHog()

const {idOrg} = useOrganisationStore();
//const {idOrg} = useOrganisationStore();
const {idProject} = useProjectStore();


Expand All @@ -86,7 +86,7 @@ const AddLinkedAccount = () => {
console.log(values)
mutate({
linked_user_origin_id: values.linkedUserIdentifier,
alias: idOrg,
alias: "", //TODO
id_project: idProject
});
setShowNewLinkedUserDialog({open: false})
Expand Down
7 changes: 6 additions & 1 deletion apps/client-ts/src/components/Provider/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ import React, { useState } from "react"
import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental"
import { QueryClientProvider, QueryClient } from "@tanstack/react-query"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import { StytchProvider } from "@stytch/nextjs"
import { createStytchUIClient } from '@stytch/nextjs/ui';

function Provider({ children }: any) {
const client = new QueryClient();
const stytch = createStytchUIClient(process.env.NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN || '');

return (
<>
<QueryClientProvider client={client}>
<ReactQueryStreamedHydration>
{children}
<StytchProvider stytch={stytch}>
{children}
</StytchProvider>
</ReactQueryStreamedHydration>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
Expand Down
Loading

1 comment on commit 55c7014

@vercel
Copy link

@vercel vercel bot commented on 55c7014 Mar 18, 2024

Choose a reason for hiding this comment

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

Please sign in to comment.