Skip to content

Commit

Permalink
Merge pull request #207 from NetApp/external-api-genai-cognito-with-a…
Browse files Browse the repository at this point in the history
…d-docs-update

external-api-genai-cognito-with-ad-docs-update
  • Loading branch information
dweiszNetapp authored Nov 24, 2024
2 parents c036f8d + cefbc2e commit 17910fb
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 103 deletions.
2 changes: 2 additions & 0 deletions AI/GenAI-ChatBot-application-sample/.env.local.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ NEXT_PUBLIC_LOGIN_PROVIDER=cognito
NEXT_PUBLIC_KNOWLEDGE_BASE_ID=YOUR_KNOWLEDGE_BASE_ID
NEXT_PUBLIC_AWS_USER_POOLS_ID=YOUR_AWS_USER_POOLS_ID
NEXT_PUBLIC_AWS_USER_WEB_CLIENT_ID=YOUR_AWS_USER_WEB_CLIENT_ID
# NEXT_PUBLIC_LOGIN_EXTERNAL_PROVIDER=AD--NAME-SAMPLE
# NEXT_PUBLIC_AWS_OAUTH={"domain":"<DOMAIN_PREFIX>.auth.<REGION>.amazoncognito.com","scope":["openid","profile","email"],"redirectSignIn":"http://localhost:9091","redirectSignOut":"http://localhost:9091","responseType":"code"}

# NEXT_PUBLIC_LOGIN_PROVIDER=clerk
# NEXT_PUBLIC_KNOWLEDGE_BASE_ID=YOUR_KNOWLEDGE_BASE_ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class AuthService {
await Auth.signUp({
username,
password,
}).then(data => {
}).then(() => {

logger.info("Registering " + username);

Expand Down Expand Up @@ -131,7 +131,7 @@ export class AuthService {
data: data
});
})
.catch(err => {
.catch(() => {
logger.error("Couldn't sign out for some reason");
Hub.dispatch(AuthService.CHANNEL, {
event: AuthService.AUTH_EVENTS.SIGN_OUT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const awsConfig = {
aws_user_pools_mfa_type: 'OFF',
aws_user_pools_web_client_id: process.env.NEXT_PUBLIC_AWS_USER_WEB_CLIENT_ID,
aws_user_settings: 'enable',
oauth: process.env.NEXT_PUBLIC_AWS_OAUTH
};

export default awsConfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ import { addNotification, removeNotification } from '@/lib/slices/notifications.
import { NOTIFICATION_CONSTS } from '../NotificationGroup/notification.consts';
import { setAuth } from '@/lib/slices/auth.slice';
import { ClerkSignIn } from '@/app/services/clerk.service';
import { SignInResult, LoginProvider } from '@/lib/api/api.types';
import {LoginProvider, SignInResultClerk, SignInResultCognito} from '@/lib/api/api.types';
import useRunUntil from '@/app/hooks/useRunUntil';

const LoginForm = () => {
export type LoginType = 'AD' | 'UserPassword'
interface LoginFormProps {
onLoginSuccess: (loginType:LoginType )=> void
}

const LoginForm = ({onLoginSuccess}:LoginFormProps) => {
const loginProvider = process.env.NEXT_PUBLIC_LOGIN_PROVIDER as LoginProvider;
const loginEternalProvider:string | undefined = process.env.NEXT_PUBLIC_LOGIN_EXTERNAL_PROVIDER;

const emailRef = useRef<HTMLInputElement>(null);

Expand All @@ -29,8 +35,8 @@ const LoginForm = () => {
const [email, setEmail] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();

const { isLoading: isLoadingCog, jwtToken: jwtTokenCog, email: emailCog, password: passwordCog, userName: userNameCog, error: errorCog, doLogin: doLoginCog } = loginProvider === 'cognito' ? CognitoSignIn() : {} as SignInResult
const { isLoading: isLoadingClerk, jwtToken: jwtTokenClerk, email: emailClerk, password: passwordClerk, userName: userNameClerk, error: errorClerk, doLogin: doLoginClerk } = loginProvider === 'clerk' ? ClerkSignIn() : {} as SignInResult;
const { isLoading: isLoadingCog, jwtToken: jwtTokenCog, email: emailCog, password: passwordCog, userName: userNameCog, error: errorCog, doLogin: doLoginCog } = loginProvider === 'cognito' ? CognitoSignIn() : {} as SignInResultCognito
const { isLoading: isLoadingClerk, jwtToken: jwtTokenClerk, email: emailClerk, password: passwordClerk, userName: userNameClerk, error: errorClerk, doLogin: doLoginClerk } = loginProvider === 'clerk' ? ClerkSignIn() : {} as SignInResultClerk;

useRunUntil(() => {
emailRef.current?.focus();
Expand All @@ -53,18 +59,19 @@ const LoginForm = () => {

useEffect(() => {
if (jwtTokenCog || jwtTokenClerk) {
onLoginSuccess(loginEternalProvider? 'AD': 'UserPassword')
dispatch(setAuth({
isSuccess: true,
accessToken: jwtTokenCog || jwtTokenClerk,
userName: userNameClerk || userNameCog
}));
router.push(`${ROUTES.BASE}${ROUTES.CHAT}`);
}
}, [jwtTokenCog, jwtTokenClerk, router, userNameClerk, userNameCog, dispatch])
}, [jwtTokenCog, jwtTokenClerk, router, userNameClerk, userNameCog, dispatch, loginEternalProvider, onLoginSuccess])

const doLogin = () => {
dispatch(removeNotification(NOTIFICATION_CONSTS.UNIQUE_IDS.USER_NOT_CONFIRMED));
loginProvider === 'cognito' ? doLoginCog(email, password) : doLoginClerk(email, password);
loginProvider === 'cognito' ? doLoginCog(email, password,loginEternalProvider) : doLoginClerk(email, password);
}

return (
Expand All @@ -73,7 +80,8 @@ const LoginForm = () => {
<div className={styles.formContent}>
<span className={_Classes(global.Regular_24, styles.formTitle)}>Log in to Workload Factory GenAI sample application</span>
<span className={`${global.Regular_14}`}>Log in to NetApp GenAI Studio chatbot sample application with<br />your company user account.</span>
<DsTextField
{!process.env.NEXT_PUBLIC_LOGIN_EXTERNAL_PROVIDER && <>
<DsTextField
ref={emailRef}
title='Email'
className={styles.formInput}
Expand All @@ -91,20 +99,21 @@ const LoginForm = () => {
value: 'This field is required.'
} : undefined}
/>
<DsTextField
title='Password'
isPassword
className={styles.formInput}
onChange={event => setPassword(event?.target.value)}
onKeyDown={event => {
if (event.key === 'Enter') {
doLogin();
}
}}
message={password === '' ? {
type: 'error',
value: 'This field is required.'
} : undefined} />
<DsTextField
title='Password'
isPassword
className={styles.formInput}
onChange={event => setPassword(event?.target.value)}
onKeyDown={event => {
if (event.key === 'Enter') {
doLogin();
}
}}
message={password === '' ? {
type: 'error',
value: 'This field is required.'
} : undefined} />
</>}
<DsButton onClick={() => doLogin()} className={styles.loginButton} isLoading={isLoadingCog || isLoadingClerk}>Login</DsButton>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
color: #404040;

&.isHidden {
opacity: 0;
}

&.font-variants {
--semibold-font-weight: 500;

Expand Down Expand Up @@ -37,7 +41,7 @@
font-size: 20px;
line-height: 32px;
font-weight: 400;
}
}

.Regular_14 {
font-size: 14px;
Expand Down
14 changes: 9 additions & 5 deletions AI/GenAI-ChatBot-application-sample/src/app/home.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
'use client';

import React from 'react';
import styles from './global.module.scss';
import LoginForm from "./components/loginForm/loginForm";
import LoginForm, { LoginType } from "./components/loginForm/loginForm";
import ChatBot from '@/app/svgs/login/chatBot.png'
import Cloud from '@/app/svgs/login/cloud.png'
import Image from 'next/image';
import { _Classes } from '@/utils/cssHelper.util';
import { useAppSelector } from '@/lib/hooks';
import rootSelector from '@/lib/selectors/root.selector';
import { useState } from "react";

const Home = () => {
const { accessToken, isSuccess } = useAppSelector(rootSelector.auth);

const [loginType, setLoginType] = useState<LoginType | undefined>(undefined);
return (
<div className={styles.genAi}>
{!accessToken && isSuccess && <>
<LoginForm />
<div className={_Classes(styles.genAi, loginType === 'AD' ? styles.isHidden : '')}>
{(!accessToken && isSuccess) && <>
<LoginForm onLoginSuccess={
setLoginType
} />
<div className={styles.welcomeContent}>
<Image
src={ChatBot}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SignInResult } from "@/lib/api/api.types";
import { SignInResultClerk } from "@/lib/api/api.types";
import { useAuth, useClerk, useSignIn, useUser } from "@clerk/nextjs";
import { useEffect, useState } from "react";

export const ClerkSignIn = (): SignInResult => {
export const ClerkSignIn = (): SignInResultClerk => {
const [email, setEmail] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import useRunOnce from "../hooks/useRunOnce";
import awsConfig from "../cognito/aws-configs";
import { useEffect, useState } from "react";
import { AuthService } from "../auth/auth-service";
import { CognitoPayload } from "../cognito/cognito.types";
import { SignInResult } from "@/lib/api/api.types";
import {CognitoPayload} from "../cognito/cognito.types";
import {SignInResultCognito} from "@/lib/api/api.types";
import {CognitoUser} from "amazon-cognito-identity-js";
import awsConfigs from "../cognito/aws-configs";

const CognitoSignIn = (): SignInResult => {
const isLoginEternalProvider:boolean = !!process.env.NEXT_PUBLIC_LOGIN_EXTERNAL_PROVIDER;

const CognitoSignIn = (): SignInResultCognito => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [email, setEmail] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
Expand All @@ -15,6 +19,7 @@ const CognitoSignIn = (): SignInResult => {
const [userName, setUserName] = useState<string | undefined>();

useRunOnce(() => {
if (awsConfig.oauth && typeof awsConfig.oauth === "string") awsConfigs.oauth = JSON.parse(awsConfigs.oauth!)
Amplify.configure(awsConfig);
})

Expand Down Expand Up @@ -52,7 +57,17 @@ const CognitoSignIn = (): SignInResult => {

const updateUser = async () => {
try {
await Auth.currentAuthenticatedUser()
const authenticatedUser:CognitoUser = await Auth.currentAuthenticatedUser()
if (isLoginEternalProvider){
Hub.dispatch(AuthService.CHANNEL, {
event: AuthService.AUTH_EVENTS.LOGIN,
// @ts-ignore
success: true,
message: "",
username: authenticatedUser.getUsername(),
user: authenticatedUser
});
}
} catch {
}
}
Expand All @@ -64,13 +79,17 @@ const CognitoSignIn = (): SignInResult => {
};
}, []);

const doLogin = async (email?: string, password?: string) => {
const doLogin = async (email?: string, password?: string,externalProviderName?:string) => {
setError(undefined);

setEmail(email || '');
setPassword(password || '');

if (email && password) {
if (externalProviderName){
setIsLoading(true);
await Auth.federatedSignIn({ provider: externalProviderName as any });
}
else if (email && password){
setIsLoading(true);
await AuthService.login(email, password);
}
Expand Down
11 changes: 9 additions & 2 deletions AI/GenAI-ChatBot-application-sample/src/lib/api/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ export type ChunkingStrategy = 'sentences' | 'words' | 'characters';
export type MessageType = 'ANSWER' | 'ERROR';
export type LoginProvider = 'cognito' | 'clerk';

export interface SignInResult {
export interface SignInResultCognito extends SignInResult {
doLogin: (email?: string, password?: string, externalProviderName?: string) => void,
}

export interface SignInResultClerk extends SignInResult {
doLogin: (email?: string, password?: string) => void,
}

interface SignInResult {
isLoading: boolean,
email?: string,
password?: string,
doLogin: (email?: string, password?: string) => void,
jwtToken?: string,
error?: string,
userName?: string
Expand Down
Loading

0 comments on commit 17910fb

Please sign in to comment.