diff --git a/README.md b/README.md index 7405f35..c5e9df9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Overview -This example application demonstrates how one may use Stytch's B2B authentication suite within a Next.js application. The application features a sign-up and login flow powered by Email magic links. On sign-up a new organization is created, and the initial member becomes the admin of that organization. As admin, the member is able to invite others to join the organization, and set up SSO connections via SAML or OIDC. +This example application demonstrates how one may use Stytch's B2B authentication suite within a Next.js application. The application features a sign-up and login flow powered by Email magic links and Google OAuth. On sign-up a new organization is created, and the initial member becomes the admin of that organization. As admin, the member is able to invite others to join the organization, and set up SSO connections via SAML or OIDC. This project utilizes Stytch's RBAC offering to enforce authorization on Stytch resources as well as custom resources. @@ -33,9 +33,12 @@ Follow the steps below to get this application fully functional and running usin 1. The 'Create Organizations' setting under 'Enabled methods'. This will enable end users to create new organizations when signing up for the application. 2. The 'Member actions & permissions' setting under 'Enabled methods'. This will allow the SDK to take various actions on behalf of the logged-in Member as long as they are permitted under the project's RBAC policy. -4. Navigate to [Roles and Permissions](https://stytch.com/dashboard/rbac) to configure the project's RBAC policy. There should already be a "stytch_member" role, which is implicitly granted to all Members, and a "stytch_admin" role. +4. **OPTIONAL** If you want to see Google OAuth in action in the login/signup forms, you'll need to configure Google OAuth in the [Stytch dashboard](https://stytch.com/dashboard/oauth/google). Note that if you are using the Stytch test environment, Google OAuth is automatically configured for you and you will not need to set anything up, but the Google One Tap prompt will not render. +If you'd like the Google One Tap prompt to appear, make sure to set up your Google OAuth credentials and follow the additional setup steps for adding an authorized Javascript origin. You'll need to add both `http://localhost` and `http://localhost:3000` as authorized origins. - 1. Click the "Roles" dropdown to navigate to "Resources". Click the "Create new Resource" button and create a resource with the resource ID = "todos". In the "actions" accordion, click the "Add action" button and input the following string: "create delete", which will create two actions. Make sure to save the resource before continuing. +5. **OPTIONAL** If you want to see RBAC in action, navigate to [Roles and Permissions](https://stytch.com/dashboard/rbac) to configure the project's RBAC policy. There should already be a "stytch_member" role, which is implicitly granted to all Members, and a "stytch_admin" role. + + 1. Click on the "Resources" tab. Click the "Create new Resource" button and create a resource with the resource ID = "todos". In the "actions" accordion, click the "Add action" button and input the following string: "create delete", which will create two actions. Make sure to save the resource before continuing. 2. Go back to the "Roles" page. Click on the "stytch_admin" role. Click "Edit Role". Under "Permissions", click "Assign permissions". In the modal, select "todos" in the resource dropdown, then toggle the "Assign the wildcard..." switch. Click "done", then save the role. 3. Go back to the "Roles" page. Click on the "stytch_member" role. Click "Edit Role". In the "permissions" accordion, you'll need to add permissions for 2 different resources. Once you've added both, click "done", then save the role. 1. For the "stytch.member" resource, add the following action: "search". @@ -45,7 +48,7 @@ Follow the steps below to get this application fully functional and running usin 2. For the "stytch.sso" resource, add the following action: "update". 3. For the "todos" resource, add the following action: "create". -5. Finally, navigate to [API Keys](https://stytch.com/dashboard/api-keys). You will need the `public_token` value found on this page later on. +6. Finally, navigate to [API Keys](https://stytch.com/dashboard/api-keys). You will need the `public_token` value found on this page later on. ### On your machine @@ -79,7 +82,11 @@ After completing all the setup steps above the application can be run with the c npm run dev ``` -The application will be available at [`http://localhost:3000`](http://localhost:3000). +The application will be available at [`http://localhost:3000`](http://localhost:3000). The "Log in" and "Sign Up" buttons will both lead to a discovery flow. +To log in to a specific organization, navigate to `http://localhost:3000/{{slug}}/login`, where `slug` is the `organization_slug` of the organization you wish to log into. +For the organization-specific flow to work, make sure to add an organization URL template to your frontend SDK configuration as specified in step #3 of setup. + +See the [Stytch docs](https://stytch.com/docs/b2b/guides/login-flows) for more information about the different types of login flows. ## Expected RBAC functionality @@ -105,6 +112,8 @@ in your RBAC policy. ### Roles and Permissions +To see all of the RBAC functionality described in this section, make sure to follow step #5 of the setup instructions above to configure your RBAC policy. + The first member in an organization will automatically be assigned the "stytch_admin" role. This role will allow them to invite new members to the organization, update member's names, create SSO connections, and update SSO connections. They will also be able to create and delete items in the TODO list, which utilizes Stytch diff --git a/components/DiscoveryForm.tsx b/components/DiscoveryForm.tsx index faf621b..5b44aac 100644 --- a/components/DiscoveryForm.tsx +++ b/components/DiscoveryForm.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { StytchB2B } from "@stytch/nextjs/b2b"; import { AuthFlowType, + B2BOAuthProviders, B2BProducts, StytchB2BUIConfig, } from "@stytch/vanilla-js"; @@ -11,12 +12,17 @@ const LoginOrSignupDiscoveryForm = () => { useEffect(() => { setConfig({ - products: [B2BProducts.emailMagicLinks], + products: [B2BProducts.emailMagicLinks, B2BProducts.oauth], sessionOptions: { sessionDurationMinutes: 60 }, emailMagicLinksOptions: { // window.location.origin is not defined on SSR - we need to wait for CSR to render discoveryRedirectURL: `${window.location.origin}/authenticate`, }, + oauthOptions: { + providers: [{ type: B2BOAuthProviders.Google, one_tap: true }], + // window.location.origin is not defined on SSR - we need to wait for CSR to render + discoveryRedirectURL: `${window.location.origin}/authenticate`, + }, authFlowType: AuthFlowType.Discovery, }); }, []); diff --git a/components/TenatedLoginForm.tsx b/components/TenatedLoginForm.tsx index c287165..537f5fe 100644 --- a/components/TenatedLoginForm.tsx +++ b/components/TenatedLoginForm.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { StytchB2B } from "@stytch/nextjs/b2b"; import { AuthFlowType, + B2BOAuthProviders, B2BProducts, StytchB2BUIConfig, } from "@stytch/vanilla-js"; @@ -11,12 +12,17 @@ const TenantedLoginForm = () => { useEffect(() => { setConfig({ - products: [B2BProducts.emailMagicLinks], + products: [B2BProducts.emailMagicLinks, B2BProducts.oauth], sessionOptions: { sessionDurationMinutes: 60 }, emailMagicLinksOptions: { loginRedirectURL: `${window.location.origin}/authenticate`, signupRedirectURL: `${window.location.origin}/authenticate`, }, + oauthOptions: { + providers: [{ type: B2BOAuthProviders.Google, one_tap: true }], + loginRedirectURL: `${window.location.origin}/authenticate`, + signupRedirectURL: `${window.location.origin}/authenticate`, + }, authFlowType: AuthFlowType.Organization, }); }, []); diff --git a/package-lock.json b/package-lock.json index 7247d7e..4fabc00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "dependencies": { "@stytch/nextjs": "^17.0.0", - "@stytch/vanilla-js": "^4.3.0", + "@stytch/vanilla-js": "^4.10.0", "@types/node": "18.13.0", "@types/react": "18.0.28", "@types/react-dom": "18.0.10", @@ -293,9 +293,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "node_modules/@stytch/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@stytch/core/-/core-2.4.0.tgz", - "integrity": "sha512-tsCioKi3BGoxxfnx/LoA4PNEO/w4n56PBLtIj27Ehc8Zu7MlIKEGgz2dpj2l9S8QivTT8RsUaZ2FDKnfoV1HKQ==", + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/@stytch/core/-/core-2.15.2.tgz", + "integrity": "sha512-/esv4fQcSSyjKozWr5trY12+gb9RekHNf/ui0HiYdispjtPEZvdQfAfpbwFO7Um4BloxivzqiG0G9avY0YmlGg==", "dependencies": { "uuid": "8.3.2" } @@ -311,11 +311,11 @@ } }, "node_modules/@stytch/vanilla-js": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@stytch/vanilla-js/-/vanilla-js-4.3.0.tgz", - "integrity": "sha512-Dbr3HLNIrp6EesSdhZb2DukslFwaK1WVKDVfKGey+JtBMQhmAs46R89a1LhwchJ/kyVi2MocMujIuEe6f+yQ/A==", + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@stytch/vanilla-js/-/vanilla-js-4.11.2.tgz", + "integrity": "sha512-1dOtx4awAQ4Lejj/Q5pnmVkWcpRswFU2P/GRh/xcFQ+hOWaKs9CqS4gGFhDB1YzsHVZw85dDE+ai0JRAS9aTPg==", "dependencies": { - "@stytch/core": "2.4.0", + "@stytch/core": "2.15.2", "@types/google-one-tap": "^1.2.0" } }, @@ -3527,9 +3527,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "@stytch/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@stytch/core/-/core-2.4.0.tgz", - "integrity": "sha512-tsCioKi3BGoxxfnx/LoA4PNEO/w4n56PBLtIj27Ehc8Zu7MlIKEGgz2dpj2l9S8QivTT8RsUaZ2FDKnfoV1HKQ==", + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/@stytch/core/-/core-2.15.2.tgz", + "integrity": "sha512-/esv4fQcSSyjKozWr5trY12+gb9RekHNf/ui0HiYdispjtPEZvdQfAfpbwFO7Um4BloxivzqiG0G9avY0YmlGg==", "requires": { "uuid": "8.3.2" } @@ -3541,11 +3541,11 @@ "requires": {} }, "@stytch/vanilla-js": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@stytch/vanilla-js/-/vanilla-js-4.3.0.tgz", - "integrity": "sha512-Dbr3HLNIrp6EesSdhZb2DukslFwaK1WVKDVfKGey+JtBMQhmAs46R89a1LhwchJ/kyVi2MocMujIuEe6f+yQ/A==", + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@stytch/vanilla-js/-/vanilla-js-4.11.2.tgz", + "integrity": "sha512-1dOtx4awAQ4Lejj/Q5pnmVkWcpRswFU2P/GRh/xcFQ+hOWaKs9CqS4gGFhDB1YzsHVZw85dDE+ai0JRAS9aTPg==", "requires": { - "@stytch/core": "2.4.0", + "@stytch/core": "2.15.2", "@types/google-one-tap": "^1.2.0" } }, diff --git a/package.json b/package.json index 3ad7302..3a772e2 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@stytch/nextjs": "^17.0.0", - "@stytch/vanilla-js": "^4.3.0", + "@stytch/vanilla-js": "^4.10.0", "@types/node": "18.13.0", "@types/react": "18.0.28", "@types/react-dom": "18.0.10", diff --git a/pages/authenticate.tsx b/pages/authenticate.tsx index 91e8154..f57698e 100644 --- a/pages/authenticate.tsx +++ b/pages/authenticate.tsx @@ -31,6 +31,8 @@ const Discovery = () => { router.push(`/${data.organization.organization_slug}/dashboard`); } else if (type === StytchEventType.B2BMagicLinkAuthenticate) { router.push(`/${data.organization.organization_slug}/dashboard`); + } else if (type === StytchEventType.B2BOAuthAuthenticate) { + router.push(`/${data.organization.organization_slug}/dashboard`); } else if (type === StytchEventType.B2BSSOAuthenticate) { router.push(`/${data.organization.organization_slug}/dashboard`); }