Skip to content

Commit

Permalink
Bug 91938: Signs out when click on Close (X) (#68)
Browse files Browse the repository at this point in the history
* Aligned Zapp.ie Authentication process with Alice

* updated authConfig redirect setting
  • Loading branch information
desousamario85 authored Feb 4, 2025
1 parent 65eba66 commit d1082d8
Show file tree
Hide file tree
Showing 11 changed files with 371 additions and 214 deletions.
2 changes: 1 addition & 1 deletion tabs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@fluentui/react": "^8.120.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/user-event": "^13.5.0",
"@yudiel/react-qr-scanner": "^2.0.8",
"@yudiel/react-qr-scanner": "^2.1.0",
"dotenv-flow": "^4.1.0",
"http-proxy-middleware": "^2.0.6",
"light-bolt11-decoder": "^3.2.0",
Expand Down
73 changes: 20 additions & 53 deletions tabs/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Routes, Route, useNavigate } from 'react-router-dom';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
// Fluent UI imports
import { Stack } from '@fluentui/react';

Expand All @@ -18,66 +18,33 @@ import Users from './Users';
import './App.css';
import Rewards from './Rewards';
import Wallet from './Wallet';
import AuthStart from './AuthStart';
import AuthEnd from './AuthEnd';


type AppProps = {
pca: IPublicClientApplication;
};

function App({ pca }: AppProps) {
// The next 3 lines are optional. This is how you configure MSAL to take advantage of the router's navigate functions when MSAL redirects between pages in your app
const navigate = useNavigate();
const navigationClient = new CustomNavigationClient(navigate);
pca.setNavigationClient(navigationClient);

return (
<MsalProvider instance={pca}>
<PageLayout>
<Stack horizontalAlign="center">
<Pages />
</Stack>
</PageLayout>
<MsalProvider instance={pca}>
<PageLayout>
<Stack horizontalAlign="center">
<Routes>
<Route path="/feed" element={<RequireAuth><Feed /></RequireAuth>} />
<Route path="/users" element={<RequireAuth><Users /></RequireAuth>} />
<Route path="/rewards" element={<RequireAuth><Rewards /></RequireAuth>} />
<Route path="/wallet" element={<RequireAuth><Wallet /></RequireAuth>} />
<Route path="/login" element={<Login />} />
<Route path="/auth-start" element={<AuthStart />} />
<Route path="/auth-end" element={<AuthEnd />} />
<Route path="*" element={<Navigate to="/feed" replace />} />
</Routes>
</Stack>
</PageLayout>
</MsalProvider>
);
}

function Pages() {
return (
<Routes>
<Route path="/" element={<Login />} />
<Route
path="/feed"
element={
<RequireAuth>
<Feed />
</RequireAuth>
}
/>
<Route
path="/users"
element={
<RequireAuth>
<Users />
</RequireAuth>
}
/>
<Route
path="/Rewards"
element={
<RequireAuth>
<Rewards />
</RequireAuth>
}
/>
<Route
path="/Wallet"
element={
<RequireAuth>
<Wallet />
</RequireAuth>
}
/>
</Routes>
);
}

export default App;
export default App;
62 changes: 62 additions & 0 deletions tabs/src/AuthEnd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useEffect } from 'react';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './services/authConfig';

const AuthEnd: React.FC = () => {
useEffect(() => {
const msalInstance = new PublicClientApplication(msalConfig);
const urlParams = new URLSearchParams(window.location.search);
const redirectUrl = urlParams.get('redirectUrl') || window.location.origin;
msalInstance.initialize().then(() => {
msalInstance
.handleRedirectPromise()
.then(response => {
window.close();
if (response) {
// Authentication was successful
window.opener.postMessage(
{
type: 'auth-success',
data: response,
},
window.location.origin,
);
if (window.opener) {
window.opener.focus();
window.close(); // Close the authentication window
if (!window.closed) {
window.location.href = redirectUrl; // Fallback to redirect if window.close() fails
}
} else {
window.location.href = redirectUrl; // Redirect to the original page
}
}
})
.catch(error => {
console.error('Error handling redirect:', error);
window.opener.postMessage(
{
type: 'auth-error',
data: error,
},
window.location.origin,
);
window.opener.focus();
window.close(); // Close the authentication window
if (!window.closed) {
window.location.href = redirectUrl; // Fallback to redirect if window.close() fails
}
});
}).catch(error => {
console.error('Error initializing MSAL:', error);
});
}, []);

return (
<div>
<h1>Completing Authentication...</h1>
</div>
);
};

export default AuthEnd;
45 changes: 45 additions & 0 deletions tabs/src/AuthStart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useEffect } from 'react';
import { PublicClientApplication, InteractionStatus } from '@azure/msal-browser';
import { useMsal } from '@azure/msal-react';
import { msalConfig } from './services/authConfig';

const AuthStart: React.FC = () => {
const { inProgress } = useMsal();

useEffect(() => {
const msalInstance = new PublicClientApplication(msalConfig);

msalInstance.initialize().then(() => {
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
// User is already signed in
if (inProgress !== InteractionStatus.Startup && inProgress !== InteractionStatus.HandleRedirect) {
window.location.href = window.location.origin + '/auth-end'; // Redirect to the auth-end page
}
} else {
// Check if an interaction is already in progress
if (inProgress !== InteractionStatus.None) {
// Initiate login
msalInstance.loginRedirect({
scopes: ['User.Read'],
redirectUri: window.location.origin + '/auth-end', // Redirect back to auth-end page
}).catch(error => {
console.error('Error during loginRedirect:', error);
});
} else {
console.log('Interaction is already in progress.');
}
}
}).catch(error => {
console.error('Error initializing MSAL:', error);
});
}, [inProgress]);

return (
<div>
<h1>Starting Authentication...</h1>
</div>
);
};

export default AuthStart;
14 changes: 8 additions & 6 deletions tabs/src/components/RequireAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { useEffect } from 'react';
import { useMsal } from '@azure/msal-react';
import { loginRequest } from '../services/authConfig';
import { useNavigate } from 'react-router-dom';

const RequireAuth = ({ children }: { children: JSX.Element }) => {
const { instance, accounts } = useMsal();
const { instance, accounts, inProgress } = useMsal();
const navigate = useNavigate();

useEffect(() => {
if (accounts.length === 0) {
instance.loginPopup(loginRequest).catch(error => {
console.error(error);
if (accounts.length === 0 && inProgress === "none") {
instance.acquireTokenSilent(loginRequest).catch(() => {
navigate('/login', { replace: true });
});
}
}, [accounts, instance]);
}, [accounts, instance, inProgress, navigate]);

return accounts.length > 0 ? children : null;
};

export default RequireAuth;
export default RequireAuth;
122 changes: 74 additions & 48 deletions tabs/src/components/SignInButton.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,94 @@
import { useState } from 'react';
import { useMsal } from '@azure/msal-react';
import {
DefaultButton,
ContextualMenu,
IContextualMenuProps,
} from '@fluentui/react';
import { DefaultButton } from '@fluentui/react';
import * as microsoftTeams from '@microsoft/teams-js';
import { loginRequest } from '../services/authConfig';

export const SignInButton = () => {
const { instance } = useMsal();

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleLogin = async () => {
const redirectUrl = window.location.href;

const handleLogin = async (loginType: string) => {
console.log('Login type: ', loginType);
try {
setAnchorEl(null);
// Initialize Teams SDK and check context
await microsoftTeams.app.initialize();
const context = await microsoftTeams.app.getContext();

if (loginType === 'popup') {
await instance.loginPopup({ ...loginRequest, prompt: 'login' });
} else if (loginType === 'redirect') {
await instance.loginRedirect({ ...loginRequest, prompt: 'login' });
}
} catch (error: any) {
if (error.errorCode === 'user_cancelled') {
console.warn('Login was cancelled. Please try again.');
if (context.app.host.clientType === 'desktop') {
console.log('Running inside Teams');

// Use the new `authentication.authenticate` method
try {
const authToken = await microsoftTeams.authentication.authenticate({
url: `${window.location.origin}/auth-start?action=login&redirectUrl=${encodeURIComponent(redirectUrl)}`,
width: 600,
height: 535,
});

console.log('Teams Auth Token:', authToken);

const accounts = instance.getAllAccounts();
if (accounts.length === 0) {
console.warn('No accounts found for silent token acquisition');
return;
}

const msalResponse = await instance.acquireTokenSilent({
scopes: ['User.Read'],
account: accounts[0],
});

console.log('MSAL Token Response:', msalResponse);
// Handle successful authentication
} catch (error) {
console.error('Error during Teams authentication:', error);

// Fallback to interactive login
try {
const msalResponse = await instance.loginPopup(loginRequest);
console.log('MSAL Token Response (interactive):', msalResponse);
} catch (interactiveError) {
console.error('Error during interactive login:', interactiveError);
}
}
} else {
console.warn('An error occurred during login. Please try again.');
console.error('Login error:', error);
console.log('Running in a web browser');

// Handle web browser authentication
try {
const msalResponse = await instance.loginPopup({
scopes: ['User.Read'],
prompt: 'select_account',
});

// Handle successful authentication
} catch (error) {
console.error('Error during loginPopup:', error);
}
}
}
};
} catch (error) {
console.error('Login error:', error);
console.log('Running in a web browser');
// Handle web browser authentication
try {
const msalResponse = await instance.loginPopup({
scopes: ['User.Read'],
prompt: 'select_account',
});

const menuProps: IContextualMenuProps = {
items: [
{
key: 'popup',
text: 'Login with Popup',
onClick: () => {
handleLogin('popup');
return undefined; // Ensure the callback is synchronous and returns void
},
},
{
key: 'redirect',
text: 'Login with Redirect',
onClick: () => {
handleLogin('redirect');
return undefined; // Ensure the callback is synchronous and returns void
},
},
],
directionalHintFixed: true,
target: anchorEl,
onDismiss: () => setAnchorEl(null),
console.log('MSAL Token Response:', msalResponse);
// Handle successful authentication
} catch (error) {
console.error('Error during loginPopup:', error);
}
}
};

return (
<div>
<DefaultButton
text="Sign In"
onClick={() => handleLogin('redirect')}
onClick={handleLogin}
styles={{
root: {
color: 'black',
Expand All @@ -71,7 +98,6 @@ export const SignInButton = () => {
},
}}
/>
{open && <ContextualMenu {...menuProps} />}
</div>
);
};
};
Loading

0 comments on commit d1082d8

Please sign in to comment.