MSAL Browser 2.28+ requires explicit initialize() before handleRedirectPromise() or loginRedirect(). Without it the authorization request is generated with missing parameters, causing AADSTS90014. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
181 lines
6.8 KiB
PHP
181 lines
6.8 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Signing In — Dow Jones Job Naming Tool</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<style>
|
|
body {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 100vh;
|
|
margin: 0;
|
|
background: var(--bg-color);
|
|
}
|
|
.auth-card {
|
|
text-align: center;
|
|
padding: 48px 40px;
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
max-width: 400px;
|
|
width: 90%;
|
|
}
|
|
.auth-card h1 {
|
|
font-size: 1.2rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
margin-bottom: 8px;
|
|
}
|
|
.auth-card p {
|
|
color: var(--text-secondary);
|
|
margin-bottom: 32px;
|
|
font-size: 0.9rem;
|
|
}
|
|
.spinner-ring {
|
|
display: inline-block;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid var(--border-color);
|
|
border-top-color: var(--text-primary);
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
margin-bottom: 20px;
|
|
}
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
.auth-status {
|
|
font-size: 0.85rem;
|
|
color: var(--text-secondary);
|
|
margin-top: 16px;
|
|
}
|
|
.auth-error {
|
|
color: var(--danger-color, #e53e3e);
|
|
font-size: 0.85rem;
|
|
margin-top: 16px;
|
|
display: none;
|
|
}
|
|
.btn-signin {
|
|
display: none;
|
|
margin-top: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="auth-card">
|
|
<h1>Dow Jones Job Naming Tool</h1>
|
|
<p>Oliver Agency</p>
|
|
<div class="spinner-ring" id="authSpinner"></div>
|
|
<div class="auth-status" id="authStatus">Signing you in…</div>
|
|
<div class="auth-error" id="authError"></div>
|
|
<button id="signInBtn" class="btn btn-primary btn-signin">Sign In with Microsoft</button>
|
|
</div>
|
|
|
|
<script src="https://alcdn.msauth.net/browser/2.32.2/js/msal-browser.min.js"></script>
|
|
<script>
|
|
(function () {
|
|
const msalConfig = {
|
|
auth: {
|
|
clientId: '<?php echo AZURE_CLIENT_ID; ?>',
|
|
authority: 'https://login.microsoftonline.com/<?php echo AZURE_TENANT_ID; ?>',
|
|
redirectUri: '<?php echo AZURE_REDIRECT_URI; ?>'
|
|
},
|
|
cache: {
|
|
cacheLocation: 'sessionStorage',
|
|
storeAuthStateInCookie: true
|
|
}
|
|
};
|
|
|
|
const loginRequest = { scopes: ['openid', 'profile', 'email'] };
|
|
|
|
const msalInstance = new msal.PublicClientApplication(msalConfig);
|
|
|
|
function showError(msg) {
|
|
document.getElementById('authSpinner').style.display = 'none';
|
|
document.getElementById('authStatus').style.display = 'none';
|
|
document.getElementById('authError').textContent = msg;
|
|
document.getElementById('authError').style.display = 'block';
|
|
document.getElementById('signInBtn').style.display = 'inline-block';
|
|
}
|
|
|
|
async function init() {
|
|
await msalInstance.initialize();
|
|
try {
|
|
// Always call handleRedirectPromise first
|
|
const result = await msalInstance.handleRedirectPromise();
|
|
|
|
if (result && result.idToken) {
|
|
// We have a token — create server session
|
|
document.getElementById('authStatus').textContent = 'Verifying…';
|
|
|
|
const resp = await fetch('api.php?action=create_session', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id_token: result.idToken })
|
|
});
|
|
|
|
const json = await resp.json();
|
|
|
|
if (json.success) {
|
|
// Restore original destination or default to index.php
|
|
const returnUrl = sessionStorage.getItem('auth_return_url') || 'index.php';
|
|
sessionStorage.removeItem('auth_return_url');
|
|
window.location.href = returnUrl;
|
|
} else {
|
|
showError('Sign-in failed: ' + (json.message || 'Unknown error'));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// No redirect result — check for cached account
|
|
const accounts = msalInstance.getAllAccounts();
|
|
if (accounts.length > 0) {
|
|
// Has cached account but no PHP session — try silent token
|
|
try {
|
|
const silentResult = await msalInstance.acquireTokenSilent({
|
|
...loginRequest,
|
|
account: accounts[0]
|
|
});
|
|
|
|
const resp = await fetch('api.php?action=create_session', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id_token: silentResult.idToken })
|
|
});
|
|
|
|
const json = await resp.json();
|
|
if (json.success) {
|
|
const returnUrl = sessionStorage.getItem('auth_return_url') || 'index.php';
|
|
sessionStorage.removeItem('auth_return_url');
|
|
window.location.href = returnUrl;
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
// Silent failed — fall through to redirect login
|
|
}
|
|
}
|
|
|
|
// No account / silent failed — store return URL and redirect to Azure
|
|
if (!sessionStorage.getItem('auth_return_url')) {
|
|
sessionStorage.setItem('auth_return_url', window.location.href);
|
|
}
|
|
await msalInstance.loginRedirect(loginRequest);
|
|
|
|
} catch (err) {
|
|
console.error('MSAL error:', err);
|
|
showError('Authentication error: ' + (err.message || String(err)));
|
|
}
|
|
}
|
|
|
|
// Manual sign-in button (shown only on error)
|
|
document.getElementById('signInBtn').addEventListener('click', async () => {
|
|
sessionStorage.setItem('auth_return_url', window.location.href);
|
|
await msalInstance.loginRedirect(loginRequest);
|
|
});
|
|
|
|
init();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|