MSAL and Security Update
This commit is contained in:
parent
fc3e43bdc5
commit
eebe7c41aa
12 changed files with 580 additions and 4 deletions
14
.env.example
14
.env.example
|
|
@ -1,3 +1,15 @@
|
|||
# Google Gemini API Key
|
||||
# Get your API key from: https://makersuite.google.com/app/apikey
|
||||
VITE_GEMINI_API_KEY=your_key_here
|
||||
VITE_GEMINI_API_KEY=AIzaSyCMKLSJJYEx4c6-TtBACnjdULrLzsr_fts
|
||||
|
||||
# Microsoft SSO Configuration Local Development Credentials
|
||||
# Client ID and Tenant ID from Azure AD App Registration
|
||||
# VITE_MSAL_CLIENT_ID=15c0c4e2-bac0-4564-a3a6-c2717f00a6d9
|
||||
# VITE_MSAL_TENANT_ID=e519c2e6-bc6d-4fdf-8d9c-923c2f002385
|
||||
# VITE_MSAL_REDIRECT_URI=http://localhost:3000
|
||||
|
||||
|
||||
# Redirect URI - CONFIGURE FOR PRODUCTION DEPLOYMENT
|
||||
VITE_MSAL_CLIENT_ID=9079054c-9620-4757-a256-23413042f1ef
|
||||
VITE_MSAL_TENANT_ID=e519c2e6-bc6d-4fdf-8d9c-923c2f002385
|
||||
VITE_MSAL_REDIRECT_URI=https://ai-sandbox.oliver.solutions/prompt/index.html
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
|
|
@ -11,10 +11,15 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.claude/
|
||||
|
||||
# Environment variables
|
||||
|
||||
# Environment variables (NEVER commit these - contain API keys and secrets)
|
||||
.env
|
||||
.env.local
|
||||
.env.development
|
||||
.env.production
|
||||
.env*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
|
@ -30,3 +35,20 @@ dist-ssr
|
|||
# Test files and folders
|
||||
TESTS/
|
||||
TESTS/*
|
||||
|
||||
# Backup files
|
||||
*.bak
|
||||
*.backup
|
||||
*.old
|
||||
*.orig
|
||||
*.save
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# Security and sensitive files
|
||||
*.key
|
||||
*.pem
|
||||
*.p12
|
||||
*.pfx
|
||||
credentials.json
|
||||
secrets.json
|
||||
|
|
|
|||
122
.htaccess
Normal file
122
.htaccess
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# ============================================
|
||||
# CinePrompt Studio - Apache Security Configuration
|
||||
# Safe version that works on most servers
|
||||
# ============================================
|
||||
|
||||
# ------------------------------
|
||||
# 1. PREVENT DIRECTORY LISTING
|
||||
# ------------------------------
|
||||
<IfModule mod_autoindex.c>
|
||||
Options -Indexes
|
||||
</IfModule>
|
||||
|
||||
# ------------------------------
|
||||
# 2. BLOCK ACCESS TO SENSITIVE FILES
|
||||
# ------------------------------
|
||||
|
||||
# Block .env files (contains API keys)
|
||||
<Files ".env">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
<Files ".env.example">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
# Block .git directory
|
||||
<Files ".git">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
<Files ".gitignore">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
# Block configuration files
|
||||
<Files "package.json">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
<Files "package-lock.json">
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</Files>
|
||||
|
||||
# ------------------------------
|
||||
# 3. SINGLE PAGE APPLICATION ROUTING (OPTIONAL)
|
||||
# Only enable if deploying built app (npm run build)
|
||||
# Comment out if causing issues
|
||||
# ------------------------------
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# Don't rewrite for actual files and directories
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
|
||||
# Redirect everything else to index.html
|
||||
RewriteRule . /index.html [L]
|
||||
</IfModule>
|
||||
|
||||
# ------------------------------
|
||||
# 4. BASIC SECURITY HEADERS (OPTIONAL)
|
||||
# Only if mod_headers is enabled
|
||||
# ------------------------------
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
# Prevent clickjacking
|
||||
Header set X-Frame-Options "SAMEORIGIN"
|
||||
|
||||
# Prevent MIME sniffing
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
|
||||
# XSS Protection
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
</IfModule>
|
||||
|
||||
# ------------------------------
|
||||
# 5. GZIP COMPRESSION (OPTIONAL)
|
||||
# Only if mod_deflate is enabled
|
||||
# ------------------------------
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css
|
||||
AddOutputFilterByType DEFLATE application/javascript text/javascript
|
||||
AddOutputFilterByType DEFLATE application/json
|
||||
</IfModule>
|
||||
36
package-lock.json
generated
36
package-lock.json
generated
|
|
@ -8,6 +8,8 @@
|
|||
"name": "cineprompt-studio",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@azure/msal-browser": "^4.27.0",
|
||||
"@azure/msal-react": "^3.0.23",
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"lucide-react": "^0.555.0",
|
||||
"react": "^19.2.0",
|
||||
|
|
@ -41,6 +43,40 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-browser": {
|
||||
"version": "4.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.27.0.tgz",
|
||||
"integrity": "sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/msal-common": "15.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-common": {
|
||||
"version": "15.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.3.tgz",
|
||||
"integrity": "sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@azure/msal-react": {
|
||||
"version": "3.0.23",
|
||||
"resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-3.0.23.tgz",
|
||||
"integrity": "sha512-tHvq441nwlJD9QfQP4ZStiw6xb2hQoujNHZhZb+wpUbImb3wyr2FF6/umhX/p+yzc/aq0Lee7mbdDDpzRZzxcA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@azure/msal-browser": "^4.27.0",
|
||||
"react": "^16.8.0 || ^17 || ^18 || ^19.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/msal-browser": "^4.27.0",
|
||||
"@azure/msal-react": "^3.0.23",
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"lucide-react": "^0.555.0",
|
||||
"react": "^19.2.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import CinePromptStudio from './components/CinePromptStudio';
|
||||
import AuthGate from './components/AuthGate';
|
||||
|
||||
function App() {
|
||||
return <CinePromptStudio />;
|
||||
return (
|
||||
<AuthGate>
|
||||
<CinePromptStudio />
|
||||
</AuthGate>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
58
src/components/AuthError.jsx
Normal file
58
src/components/AuthError.jsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { AlertCircle, RefreshCw, LogOut } from 'lucide-react';
|
||||
|
||||
function AuthError({ error, onRetry, onLogout }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 flex items-center justify-center p-4">
|
||||
<div className="max-w-md w-full bg-slate-900 rounded-lg shadow-xl p-8 border border-red-500/20">
|
||||
<div className="flex items-center justify-center mb-6">
|
||||
<div className="bg-red-500/10 p-4 rounded-full">
|
||||
<AlertCircle className="w-12 h-12 text-red-500" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold text-white text-center mb-4">
|
||||
Authentication Failed
|
||||
</h2>
|
||||
|
||||
<p className="text-slate-400 text-center mb-6">
|
||||
{error?.message || "An error occurred during authentication. Please try again."}
|
||||
</p>
|
||||
|
||||
{error?.errorCode && (
|
||||
<div className="bg-slate-800 rounded p-3 mb-6">
|
||||
<p className="text-xs text-slate-500 mb-1">Error Code:</p>
|
||||
<p className="text-sm text-slate-300 font-mono">{error.errorCode}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<button
|
||||
onClick={onRetry}
|
||||
className="flex items-center justify-center gap-2 w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-5 h-5" />
|
||||
Retry Authentication
|
||||
</button>
|
||||
|
||||
{onLogout && (
|
||||
<button
|
||||
onClick={onLogout}
|
||||
className="flex items-center justify-center gap-2 w-full bg-slate-800 hover:bg-slate-700 text-slate-300 font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
>
|
||||
<LogOut className="w-5 h-5" />
|
||||
Clear Session
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-slate-800">
|
||||
<p className="text-xs text-slate-500 text-center">
|
||||
If the problem persists, please contact your administrator or check your network connection.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthError;
|
||||
191
src/components/AuthGate.jsx
Normal file
191
src/components/AuthGate.jsx
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
import { useEffect, useState, useRef } from 'react';
|
||||
import { useMsal, useIsAuthenticated } from '@azure/msal-react';
|
||||
import { InteractionStatus } from '@azure/msal-browser';
|
||||
import { loginRequest } from '../config/msalConfig';
|
||||
import LoginPage from './LoginPage';
|
||||
import AuthError from './AuthError';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
function AuthGate({ children }) {
|
||||
const { instance, inProgress } = useMsal();
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
const [error, setError] = useState(null);
|
||||
const [isInitializing, setIsInitializing] = useState(true);
|
||||
const initializationAttempted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeAuth = async () => {
|
||||
// Prevent multiple initialization attempts
|
||||
if (initializationAttempted.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for any ongoing interaction to complete
|
||||
if (inProgress !== InteractionStatus.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
initializationAttempted.current = true;
|
||||
|
||||
try {
|
||||
// Clear any stale interaction state on mount
|
||||
const keys = Object.keys(sessionStorage);
|
||||
keys.forEach(key => {
|
||||
if (key.includes('interaction.status')) {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for MSAL to finish initialization
|
||||
await instance.initialize();
|
||||
|
||||
// Handle redirect promise (for returning from Microsoft login)
|
||||
const redirectResponse = await instance.handleRedirectPromise();
|
||||
|
||||
if (redirectResponse) {
|
||||
console.log('Login successful via redirect');
|
||||
setIsInitializing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// If not authenticated and no interaction in progress, attempt silent SSO
|
||||
if (!isAuthenticated) {
|
||||
const accounts = instance.getAllAccounts();
|
||||
|
||||
if (accounts.length > 0) {
|
||||
try {
|
||||
// Attempt silent SSO with existing account
|
||||
console.log('Attempting silent SSO...');
|
||||
await instance.ssoSilent({
|
||||
...loginRequest,
|
||||
account: accounts[0],
|
||||
});
|
||||
console.log('Silent SSO successful');
|
||||
} catch (silentError) {
|
||||
// Silent SSO failed - user will need to login interactively
|
||||
console.log('Silent SSO failed:', silentError.errorCode || silentError.message);
|
||||
// Not setting error here as this is expected for new users
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setIsInitializing(false);
|
||||
} catch (err) {
|
||||
console.error('Auth initialization error:', err);
|
||||
setError(err);
|
||||
setIsInitializing(false);
|
||||
}
|
||||
};
|
||||
|
||||
initializeAuth();
|
||||
}, [instance, isAuthenticated, inProgress]);
|
||||
|
||||
const clearInteractionState = () => {
|
||||
try {
|
||||
// Clear any stale interaction state from session storage
|
||||
const keys = Object.keys(sessionStorage);
|
||||
keys.forEach(key => {
|
||||
if (key.includes('interaction.status')) {
|
||||
sessionStorage.removeItem(key);
|
||||
console.log('Cleared stale interaction state:', key);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('Failed to clear interaction state:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogin = async () => {
|
||||
try {
|
||||
// Clear any stale interaction state first
|
||||
clearInteractionState();
|
||||
|
||||
// Give MSAL a moment to update its internal state
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// Check if interaction is still in progress after clearing
|
||||
if (inProgress !== InteractionStatus.None) {
|
||||
console.warn('Authentication still in progress after clearing state. Forcing clear...');
|
||||
// Force clear and try again
|
||||
clearInteractionState();
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
setError(null);
|
||||
console.log('Starting login redirect...');
|
||||
// Use redirect flow for authentication
|
||||
await instance.loginRedirect(loginRequest);
|
||||
} catch (err) {
|
||||
console.error('Login error:', err);
|
||||
|
||||
// If it's an interaction_in_progress error, clear state and set a helpful error
|
||||
if (err.errorCode === 'interaction_in_progress') {
|
||||
clearInteractionState();
|
||||
setError({
|
||||
...err,
|
||||
message: 'Authentication was interrupted. Please try again.',
|
||||
errorCode: 'interaction_in_progress'
|
||||
});
|
||||
} else {
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleRetry = () => {
|
||||
setError(null);
|
||||
// Clear interaction state before retrying
|
||||
clearInteractionState();
|
||||
// Wait a bit before retrying
|
||||
setTimeout(() => {
|
||||
handleLogin();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
setError(null);
|
||||
// Clear interaction state before logout
|
||||
clearInteractionState();
|
||||
await instance.logoutRedirect();
|
||||
} catch (err) {
|
||||
console.error('Logout error:', err);
|
||||
// Clear all session storage and force page reload
|
||||
sessionStorage.clear();
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
// Show loading state during initialization or authentication
|
||||
if (isInitializing || inProgress !== InteractionStatus.None) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<Loader2 className="w-12 h-12 text-blue-500 animate-spin mx-auto mb-4" />
|
||||
<p className="text-slate-400">Authenticating...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show error if authentication failed
|
||||
if (error) {
|
||||
return (
|
||||
<AuthError
|
||||
error={error}
|
||||
onRetry={handleRetry}
|
||||
onLogout={handleLogout}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Show login page if not authenticated
|
||||
if (!isAuthenticated) {
|
||||
return <LoginPage onLogin={handleLogin} />;
|
||||
}
|
||||
|
||||
// User is authenticated - render the protected content
|
||||
return children;
|
||||
}
|
||||
|
||||
export default AuthGate;
|
||||
53
src/components/LoginPage.jsx
Normal file
53
src/components/LoginPage.jsx
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { LogIn, Shield } from 'lucide-react';
|
||||
|
||||
function LoginPage({ onLogin }) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 flex items-center justify-center p-4">
|
||||
<div className="max-w-md w-full">
|
||||
<div className="text-center mb-8">
|
||||
<div className="flex items-center justify-center mb-4">
|
||||
<div className="bg-gradient-to-br from-blue-500 to-purple-600 p-4 rounded-2xl shadow-lg">
|
||||
<Shield className="w-12 h-12 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold text-white mb-2">
|
||||
Prompt Studio
|
||||
</h1>
|
||||
<p className="text-slate-400">
|
||||
AI-Powered Cinematography Prompts
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-slate-900 rounded-lg shadow-xl p-8 border border-slate-800">
|
||||
<h2 className="text-xl font-semibold text-white mb-2 text-center">
|
||||
Welcome
|
||||
</h2>
|
||||
<p className="text-slate-400 text-center mb-6 text-sm">
|
||||
Sign in with your Microsoft account to continue
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={onLogin}
|
||||
className="flex items-center justify-center gap-3 w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white font-medium py-4 px-6 rounded-lg transition-all transform hover:scale-[1.02] shadow-lg"
|
||||
>
|
||||
<LogIn className="w-5 h-5" />
|
||||
Sign in with Microsoft
|
||||
</button>
|
||||
|
||||
<div className="mt-6 pt-6 border-t border-slate-800">
|
||||
<div className="flex items-center gap-2 text-xs text-slate-500">
|
||||
<Shield className="w-4 h-4" />
|
||||
<span>Secured by Microsoft Azure Active Directory</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-600 text-center mt-6">
|
||||
By signing in, you agree to use this application in accordance with your organization's policies.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LoginPage;
|
||||
63
src/config/msalConfig.js
Normal file
63
src/config/msalConfig.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// MSAL Configuration for Microsoft SSO Authentication
|
||||
// Environment variables are loaded from .env file (must be prefixed with VITE_)
|
||||
|
||||
// Validate required environment variables
|
||||
const clientId = import.meta.env.VITE_MSAL_CLIENT_ID;
|
||||
const tenantId = import.meta.env.VITE_MSAL_TENANT_ID;
|
||||
const redirectUri = import.meta.env.VITE_MSAL_REDIRECT_URI;
|
||||
|
||||
if (!clientId || !tenantId || !redirectUri) {
|
||||
console.error('MSAL Configuration Error: Missing required environment variables');
|
||||
console.error('Required variables:');
|
||||
console.error(`- VITE_MSAL_CLIENT_ID: ${clientId ? '✓' : '✗ MISSING'}`);
|
||||
console.error(`- VITE_MSAL_TENANT_ID: ${tenantId ? '✓' : '✗ MISSING'}`);
|
||||
console.error(`- VITE_MSAL_REDIRECT_URI: ${redirectUri ? '✓' : '✗ MISSING'}`);
|
||||
console.error('Please check your .env file and ensure all MSAL variables are set.');
|
||||
}
|
||||
|
||||
export const msalConfig = {
|
||||
auth: {
|
||||
clientId: clientId || "",
|
||||
authority: `https://login.microsoftonline.com/${tenantId || ""}`,
|
||||
redirectUri: redirectUri || window.location.origin,
|
||||
},
|
||||
cache: {
|
||||
cacheLocation: "sessionStorage", // Store tokens in session storage
|
||||
storeAuthStateInCookie: true, // Set to true for IE11 or Edge compatibility
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
loggerCallback: (level, message, containsPii) => {
|
||||
if (containsPii) {
|
||||
return;
|
||||
}
|
||||
switch (level) {
|
||||
case 0: // Error
|
||||
console.error('[MSAL Error]', message);
|
||||
break;
|
||||
case 1: // Warning
|
||||
console.warn('[MSAL Warning]', message);
|
||||
break;
|
||||
case 2: // Info
|
||||
console.info('[MSAL Info]', message);
|
||||
break;
|
||||
case 3: // Verbose
|
||||
console.debug('[MSAL Debug]', message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Scopes for login request
|
||||
export const loginRequest = {
|
||||
scopes: ["User.Read"], // Basic authentication - read user profile
|
||||
};
|
||||
|
||||
// Optional: Token request for API calls
|
||||
export const tokenRequest = {
|
||||
scopes: ["User.Read"],
|
||||
};
|
||||
10
src/main.jsx
10
src/main.jsx
|
|
@ -1,10 +1,18 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { PublicClientApplication } from '@azure/msal-browser'
|
||||
import { MsalProvider } from '@azure/msal-react'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
import { msalConfig } from './config/msalConfig'
|
||||
|
||||
// Initialize MSAL instance
|
||||
const msalInstance = new PublicClientApplication(msalConfig);
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
<MsalProvider instance={msalInstance}>
|
||||
<App />
|
||||
</MsalProvider>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,4 +4,8 @@ import react from '@vitejs/plugin-react'
|
|||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 3000,
|
||||
strictPort: true,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue