Skip to content

Commit

Permalink
Add support for CAS auth strategy (#2246)
Browse files Browse the repository at this point in the history
* add cas auth strategy

* fix test case

---------

Co-authored-by: Richard Dominick <[email protected]>
  • Loading branch information
chownces and RichDom2185 authored Nov 30, 2023
1 parent 283dc3d commit 6ebe68e
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 11 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ REACT_APP_OAUTH2_PROVIDER3_ENDPOINT=http://localhost:8000/login?provider=test&co
# REACT_APP_OAUTH2_PROVIDER2_NAME=Cognito
# REACT_APP_OAUTH2_PROVIDER2_ENDPOINT=

REACT_APP_CAS_PROVIDER1=
REACT_APP_CAS_PROVIDER1_NAME=
REACT_APP_CAS_PROVIDER1_ENDPOINT=

REACT_APP_MODULE_BACKEND_URL=https://source-academy.github.io/modules
REACT_APP_SHAREDB_BACKEND_URL=
REACT_APP_SICPJS_BACKEND_URL="http://127.0.0.1:8080/"
Expand Down
5 changes: 3 additions & 2 deletions src/commons/sagas/__tests__/BackendSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ import {
import { mockGradingSummary } from '../../mocks/GradingMocks';
import { mockNotifications } from '../../mocks/UserMocks';
import { Notification } from '../../notificationBadge/NotificationBadgeTypes';
import { computeRedirectUri } from '../../utils/AuthHelper';
import { AuthProviderType, computeRedirectUri } from '../../utils/AuthHelper';
import Constants from '../../utils/Constants';
import {
showSuccessMessage,
Expand Down Expand Up @@ -286,7 +286,8 @@ describe('Test FETCH_AUTH action', () => {
Constants.authProviders.set(providerId, {
name: providerId,
endpoint: `https://test/?client_id=${clientId}`,
isDefault: true
isDefault: true,
type: AuthProviderType.OAUTH2
});
const redirectUrl = computeRedirectUri(providerId);

Expand Down
14 changes: 13 additions & 1 deletion src/commons/utils/AuthHelper.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import Constants from './Constants';

export enum AuthProviderType {
OAUTH2 = 'OAUTH2',
CAS = 'CAS'
}

export function computeEndpointUrl(providerId: string): string | undefined {
const ep = Constants.authProviders.get(providerId);
if (!ep) {
return undefined;
}
try {
const epUrl = new URL(ep.endpoint);
epUrl.searchParams.set('redirect_uri', computeRedirectUri(providerId)!);
switch (ep.type) {
case AuthProviderType.OAUTH2:
epUrl.searchParams.set('redirect_uri', computeRedirectUri(providerId)!);
break;
case AuthProviderType.CAS:
epUrl.searchParams.set('service', computeRedirectUri(providerId)!);
break;
}
return epUrl.toString();
} catch (e) {
// in dev, sometimes the endpoint is a dummy; allow that
Expand Down
22 changes: 19 additions & 3 deletions src/commons/utils/Constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Chapter, Variant } from 'js-slang/dist/types';

import { AuthProviderType } from './AuthHelper';

function isTrue(value?: string, defaultTo?: boolean): boolean {
return typeof value === 'undefined' && typeof defaultTo !== 'undefined'
? defaultTo
Expand Down Expand Up @@ -46,8 +48,10 @@ const caFulfillmentLevel = isTest
? 24
: parseInt(process.env.REACT_APP_CA_FULFILLMENT_LEVEL || '0');

const authProviders: Map<string, { name: string; endpoint: string; isDefault: boolean }> =
new Map();
const authProviders: Map<
string,
{ name: string; endpoint: string; isDefault: boolean; type: AuthProviderType }
> = new Map();

for (let i = 1; ; ++i) {
const id = process.env[`REACT_APP_OAUTH2_PROVIDER${i}`];
Expand All @@ -58,7 +62,19 @@ for (let i = 1; ; ++i) {
const name = process.env[`REACT_APP_OAUTH2_PROVIDER${i}_NAME`] || 'Unnamed provider';
const endpoint = process.env[`REACT_APP_OAUTH2_PROVIDER${i}_ENDPOINT`] || '';

authProviders.set(id, { name, endpoint, isDefault: i === 1 });
authProviders.set(id, { name, endpoint, isDefault: i === 1, type: AuthProviderType.OAUTH2 });
}

for (let i = 1; ; ++i) {
const id = process.env[`REACT_APP_CAS_PROVIDER${i}`];
if (!id) {
break;
}

const name = process.env[`REACT_APP_CAS_PROVIDER${i}_NAME`] || 'Unnamed provider';
const endpoint = process.env[`REACT_APP_CAS_PROVIDER${i}_ENDPOINT`] || '';

authProviders.set(id, { name, endpoint, isDefault: false, type: AuthProviderType.CAS });
}

export enum Links {
Expand Down
13 changes: 8 additions & 5 deletions src/pages/login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ const Login: React.FunctionComponent<{}> = () => {
const location = useLocation();
const { isLoggedIn, courseId } = useSession();
const navigate = useNavigate();
const { code, provider: providerId } = parseQuery(location.search);
const { code, ticket, provider: providerId } = parseQuery(location.search);

// `code` parameter from OAuth2 redirect, `ticket` from CAS redirect
const authCode = code || ticket;

const handleLogin = React.useCallback(
(providerId: string) => dispatch(login(providerId)),
Expand All @@ -49,12 +52,12 @@ const Login: React.FunctionComponent<{}> = () => {
}

// Else fetch JWT tokens and user info from backend when auth provider code is present
if (code && !isLoggedIn) {
dispatch(fetchAuth(code, providerId));
if (authCode && !isLoggedIn) {
dispatch(fetchAuth(authCode, providerId));
}
}, [code, providerId, dispatch, courseId, navigate, isLoggedIn]);
}, [authCode, providerId, dispatch, courseId, navigate, isLoggedIn]);

if (code) {
if (authCode) {
return (
<div className={classNames('Login', Classes.DARK)}>
<Card className={classNames('login-card', Classes.ELEVATION_4)}>
Expand Down

0 comments on commit 6ebe68e

Please sign in to comment.