Skip to content

Commit 4cc92b4

Browse files
author
Rajat
committed
Google Auth
1 parent 7d9285b commit 4cc92b4

File tree

44 files changed

+2898
-699
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2898
-699
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Use **Batching** with `bulkWrite` (e.g., batches of 500) to maximize performance and minimize network roundtrips.
1919
- Ensure **Idempotency** (safe to re-run) by using upserts or `$setOnInsert` where applicable.
2020
- When making changes to the structure of the Course, consider how it affects its representation on its public page (`apps/web/app/(with-contexts)/(with-layout)/p/[id]/page.tsx`) and the course viewer (`apps/web/app/(with-contexts)/course/[slug]/[id]/page.tsx`).
21+
- `apps/web` is a multi-tenant app.
2122

2223
### Workspace map (core modules):
2324

54.3 KB
Loading
52.4 KB
Loading
-27.7 KB
Loading

apps/docs/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export const SIDEBAR: Sidebar = {
123123
{ text: "Use custom domain", link: "en/schools/add-custom-domain" },
124124
{ text: "Set up payments", link: "en/schools/set-up-payments" },
125125
{ text: "Single Sign-On", link: "en/schools/sso" },
126+
{ text: "Sign in with Google", link: "en/schools/google-sign-in" },
126127
{ text: "Delete a school", link: "en/schools/delete" },
127128
],
128129
Users: [
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
title: Set up Sign in with Google
3+
description: Learn how to let your customers sign in with their Google accounts
4+
layout: ../../../layouts/MainLayout.astro
5+
---
6+
7+
Using Google sign-in, you can let customers authenticate with their Google accounts on your login page and checkout page.
8+
9+
## Steps to set up Google sign-in
10+
11+
1. In the CourseLit dashboard, go to `Settings` -> `Miscellaneous` -> `Login providers`.
12+
13+
![Login providers area](/assets/schools/login-providers-area.png)
14+
15+
2. Click on the Cog icon next to the Google provider to open its configuration screen.
16+
17+
3. Keep the `School Settings` card open. You will use these values while creating the Google OAuth app:
18+
19+
- **Authorized redirect URI**: Usually `https://<school-url>/api/auth/sso/callback/google`
20+
- **Authorized JavaScript origin**: Usually `https://<school-url>`
21+
22+
4. Open the [Google Cloud Console](https://console.cloud.google.com/) and create or select the project you want to use.
23+
24+
5. If prompted, configure the OAuth consent screen first:
25+
26+
- Choose the appropriate user type for your use case.
27+
- Add the app name, support email, and authorized domain details requested by Google.
28+
- If your app is in testing mode, add the Google accounts you want to use as test users.
29+
30+
6. In Google Cloud Console, go to `APIs & Services` -> `Credentials` and click `Create Credentials` -> `OAuth client ID`.
31+
32+
7. Select `Web application` as the application type.
33+
34+
8. In the OAuth client configuration screen, use the values from CourseLit:
35+
36+
- Add the `Authorized JavaScript origin` shown in CourseLit.
37+
- Add the `Authorized redirect URI` shown in CourseLit.
38+
39+
9. Click `Create`, then copy the generated `Client ID` and `Client secret`.
40+
41+
10. Return to CourseLit and paste those values into the `Google App Configuration` card.
42+
43+
11. Click `Save`.
44+
45+
12. Go back to the `Login providers` screen and enable the Google provider.
46+
47+
## Customer's experience
48+
49+
When Google login is configured and enabled, customers will see a `Continue with Google` button anywhere external login providers are shown, such as the login page and checkout page.
50+
51+
![Google login button](/assets/schools/google-login-button.png)
52+
53+
## Before you disable Google sign-in
54+
55+
Disabling the Google provider does not delete the users who previously signed up with Google. Their CourseLit account, purchases, and progress remain intact.
56+
57+
However, they will no longer be able to use `Continue with Google` to access that account. To keep signing in, they need another enabled login method that maps to the same email address, such as email login.
58+
59+
## Troubleshooting
60+
61+
### 1. I get a redirect URI mismatch error
62+
63+
Make sure the `Authorized redirect URI` in Google Cloud Console exactly matches the value shown in CourseLit, including the protocol (`https://`) and the full path.
64+
65+
### 2. I get an origin mismatch error
66+
67+
Make sure the `Authorized JavaScript origin` in Google Cloud Console exactly matches the value shown in CourseLit.
68+
69+
### 3. Only some Google accounts can sign in
70+
71+
If your OAuth app is still in testing mode, Google only allows the accounts listed as test users to sign in. Add the required accounts as test users or publish the app when you are ready.
72+
73+
## Stuck somewhere?
74+
75+
We are always here for you. Come chat with us in our <a href="https://discord.com/invite/GR4bQsN" target="_blank">Discord</a> channel or send a tweet at <a href="https://twitter.com/courselit" target="_blank">@CourseLit</a>.

apps/docs/src/pages/en/schools/sso.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,19 @@ To use this feature on [courseLit.app](https://courselit.app), you need to be on
7676
![Okta IdP metadata](/assets/schools/idp/okta/saml-signing-certificates.png)
7777

7878
9. Enter the values obtained in the `IDP Configuration` panel.
79-
10. The Okta IdP is now configured.
79+
10. The Okta IdP is now configured. Test the integration.
80+
81+
### Microsoft Entra ID
82+
83+
1. Go to `Azure` portal, search for `Enterprise applications` and click on it to go to the apps.
84+
2. Click `New application` to create a new app.
85+
3. In the `Browse Microsoft Entra App Gallery` screen, click `Create your own application`. In the settings pane, enter the app's name and click `Create`.
86+
4. After the application is created, select `Set up single sign on` and choose `SAML` as the single sign-on method.
87+
5. In the SAML setup screen, click the pencil icon in the `Basic SAML Configuration` card and enter CourseLit's `Audience URI (SP Entity ID)` in `Identifier (Entity ID)` and the `SAML ACS URL` in `Reply URL (Assertion Consumer Service URL)`.
88+
6. Next, obtain the IdP details from the `SAML Certificates` pane (on the same page). Click the pencil icon to open the settings pane, download the PEM certificate and the Federated certificate XML, and paste them into CourseLit's `Certificate` and `IDP Metadata` fields respectively.
89+
![Microsoft Entra ID's certificates](/assets/schools/idp/entra-id/sso-entra-idp-cert-and-metadata.png)
90+
7. Obtain the `Login URL` from the same screen and enter it into CourseLit's `Entry point` field.
91+
8. The Entra IdP is now configured — test the integration.
8092

8193
## Customer's experience
8294

apps/web/.env

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@
2727
# SEQUENCE_DELAY_BETWEEN_MAILS = 86400000 # 1 day in milliseconds
2828

2929
# Cache directory
30-
# CACHE_DIR=/tmp
30+
# CACHE_DIR=/tmp
31+
32+
# If using standalone Mongo (no replica set)
33+
# DB_TRANSACTION=false

apps/web/app/(with-contexts)/(with-layout)/checkout/product.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { FetchBuilder } from "@courselit/utils";
99
import { TOAST_TITLE_ERROR } from "@ui-config/strings";
1010
import { useSearchParams } from "next/navigation";
1111
import { useCallback, useContext, useEffect, useState } from "react";
12-
import type { SSOProvider } from "../login/page";
12+
import type { RuntimeLoginProvider } from "@/lib/login-providers";
1313

1414
const { MembershipEntityType } = Constants;
1515

@@ -23,7 +23,9 @@ export default function ProductCheckout() {
2323
const [product, setProduct] = useState<Product | null>(null);
2424
const [paymentPlans, setPaymentPlans] = useState<PaymentPlan[]>([]);
2525
const [includedProducts, setIncludedProducts] = useState<Course[]>([]);
26-
const [ssoProvider, setSSOProvider] = useState<SSOProvider | undefined>();
26+
const [loginProviders, setLoginProviders] = useState<
27+
RuntimeLoginProvider[]
28+
>([]);
2729

2830
const getIncludedProducts = useCallback(async () => {
2931
const query = `
@@ -97,9 +99,12 @@ export default function ProductCheckout() {
9799
}
98100
defaultPaymentPlan
99101
}
100-
ssoProvider: getSSOProvider {
102+
loginProviders: getExternalLoginProviders {
103+
key
101104
providerId
102-
domain
105+
label
106+
buttonText
107+
authType
103108
}
104109
}
105110
`;
@@ -126,9 +131,7 @@ export default function ProductCheckout() {
126131
description: "Course not found",
127132
});
128133
}
129-
if (response.ssoProvider) {
130-
setSSOProvider(response.ssoProvider);
131-
}
134+
setLoginProviders(response.loginProviders || []);
132135
} catch (err: any) {
133136
toast({
134137
title: TOAST_TITLE_ERROR,
@@ -164,9 +167,12 @@ export default function ProductCheckout() {
164167
joiningReasonText
165168
defaultPaymentPlan
166169
}
167-
ssoProvider: getSSOProvider {
170+
loginProviders: getExternalLoginProviders {
171+
key
168172
providerId
169-
domain
173+
label
174+
buttonText
175+
authType
170176
}
171177
}
172178
`;
@@ -194,9 +200,7 @@ export default function ProductCheckout() {
194200
description: "Community not found",
195201
});
196202
}
197-
if (response.ssoProvider) {
198-
setSSOProvider(response.ssoProvider);
199-
}
203+
setLoginProviders(response.loginProviders || []);
200204
} catch (err: any) {
201205
toast({
202206
title: TOAST_TITLE_ERROR,
@@ -231,7 +235,7 @@ export default function ProductCheckout() {
231235
product={product}
232236
paymentPlans={paymentPlans}
233237
includedProducts={includedProducts}
234-
ssoProvider={ssoProvider}
238+
loginProviders={loginProviders}
235239
type={entityType as MembershipEntityType | undefined}
236240
id={entityId as string | undefined}
237241
/>

apps/web/app/(with-contexts)/(with-layout)/login/login-form.tsx

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,15 @@ import { Constants, Profile } from "@courselit/common-models";
3737
import { getUserProfile } from "../../helpers";
3838
import { ADMIN_PERMISSIONS } from "@ui-config/constants";
3939
import { authClient } from "@/lib/auth-client";
40+
import type { RuntimeLoginProvider } from "@/lib/login-providers";
41+
import ExternalLoginButton from "@/components/auth/external-login-button";
4042

4143
export default function LoginForm({
4244
redirectTo,
43-
ssoProvider,
45+
loginProviders = [],
4446
}: {
4547
redirectTo?: string;
46-
ssoProvider?: {
47-
providerId: string;
48-
domain: string;
49-
};
48+
loginProviders?: RuntimeLoginProvider[];
5049
}) {
5150
const { theme } = useContext(ThemeContext);
5251
const [showCode, setShowCode] = useState(false);
@@ -312,23 +311,20 @@ export default function LoginForm({
312311
)}
313312
</>
314313
)}
315-
{siteinfo.logins?.includes(
316-
Constants.LoginProvider.SSO,
317-
) &&
318-
ssoProvider && (
319-
<Button
320-
variant="outline"
321-
onClick={async () => {
322-
await authClient.signIn.sso({
323-
providerId: ssoProvider.providerId,
324-
callbackURL: "/dashboard",
325-
});
326-
}}
327-
className="w-full lg:w-[360px] mx-auto"
328-
>
329-
Login with SSO
330-
</Button>
331-
)}
314+
{loginProviders.map((provider) => (
315+
<ExternalLoginButton
316+
key={provider.key}
317+
provider={provider}
318+
theme={theme.theme}
319+
className="w-full lg:w-[360px] mx-auto"
320+
onClick={async () => {
321+
await authClient.signIn.sso({
322+
providerId: provider.providerId,
323+
callbackURL: "/dashboard",
324+
});
325+
}}
326+
/>
327+
))}
332328
<Caption theme={theme.theme} className="text-center">
333329
{LOGIN_FORM_DISCLAIMER}
334330
<Link href="/p/terms">

0 commit comments

Comments
 (0)