fix auth
This commit is contained in:
parent
27b94edcfa
commit
e8daa37d08
@ -1,5 +1,7 @@
|
|||||||
|
// src/pages/api/auth/login.ts (ENHANCED - Consistent cookie handling)
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import { generateAuthUrl, generateState, logAuthEvent } from '../../../utils/auth.js';
|
import { generateAuthUrl, generateState, logAuthEvent } from '../../../utils/auth.js';
|
||||||
|
import { serialize } from 'cookie';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
@ -8,14 +10,28 @@ export const GET: APIRoute = async ({ url, redirect }) => {
|
|||||||
const state = generateState();
|
const state = generateState();
|
||||||
const authUrl = generateAuthUrl(state);
|
const authUrl = generateAuthUrl(state);
|
||||||
|
|
||||||
console.log('Generated auth URL:', authUrl);
|
console.log('[AUTH] Generated auth URL:', authUrl);
|
||||||
|
|
||||||
const returnTo = url.searchParams.get('returnTo') || '/';
|
const returnTo = url.searchParams.get('returnTo') || '/';
|
||||||
|
|
||||||
logAuthEvent('Login initiated', { returnTo, authUrl });
|
logAuthEvent('Login initiated', { returnTo, authUrl });
|
||||||
|
|
||||||
const stateData = JSON.stringify({ state, returnTo });
|
const stateData = JSON.stringify({ state, returnTo });
|
||||||
const stateCookie = `auth_state=${encodeURIComponent(stateData)}; HttpOnly; SameSite=Lax; Path=/; Max-Age=600`;
|
|
||||||
|
// Use consistent cookie serialization (same as session cookies)
|
||||||
|
const publicBaseUrl = process.env.PUBLIC_BASE_URL || '';
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isSecure = publicBaseUrl.startsWith('https://') || isProduction;
|
||||||
|
|
||||||
|
const stateCookie = serialize('auth_state', stateData, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: isSecure,
|
||||||
|
sameSite: 'lax',
|
||||||
|
maxAge: 600, // 10 minutes
|
||||||
|
path: '/'
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('[AUTH] Setting auth state cookie:', stateCookie.substring(0, 50) + '...');
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// src/pages/api/auth/process.ts (FIXED - Proper cookie handling)
|
// src/pages/api/auth/process.ts (ENHANCED - Proper auth success indication)
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import {
|
import {
|
||||||
verifyAuthState,
|
verifyAuthState,
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
createSessionWithCookie,
|
createSessionWithCookie,
|
||||||
logAuthEvent
|
logAuthEvent
|
||||||
} from '../../../utils/auth.js';
|
} from '../../../utils/auth.js';
|
||||||
import { apiError, apiSpecial, apiWithHeaders, handleAPIRequest } from '../../../utils/api.js';
|
import { apiError, apiSpecial, handleAPIRequest } from '../../../utils/api.js';
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
@ -30,9 +30,15 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
const stateVerification = verifyAuthState(request, state);
|
const stateVerification = verifyAuthState(request, state);
|
||||||
if (!stateVerification.isValid || !stateVerification.stateData) {
|
if (!stateVerification.isValid || !stateVerification.stateData) {
|
||||||
|
logAuthEvent('State verification failed', {
|
||||||
|
error: stateVerification.error,
|
||||||
|
hasStateData: !!stateVerification.stateData
|
||||||
|
});
|
||||||
return apiError.badRequest(stateVerification.error || 'Invalid state parameter');
|
return apiError.badRequest(stateVerification.error || 'Invalid state parameter');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[AUTH] State verification successful, exchanging code for tokens');
|
||||||
|
|
||||||
const tokens = await exchangeCodeForTokens(code);
|
const tokens = await exchangeCodeForTokens(code);
|
||||||
const userInfo = await getUserInfo(tokens.access_token);
|
const userInfo = await getUserInfo(tokens.access_token);
|
||||||
|
|
||||||
@ -43,6 +49,13 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
email: sessionResult.userEmail
|
email: sessionResult.userEmail
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add auth success indicator to the return URL
|
||||||
|
const returnUrl = new URL(stateVerification.stateData.returnTo, request.url);
|
||||||
|
returnUrl.searchParams.set('auth', 'success');
|
||||||
|
const redirectUrl = returnUrl.toString();
|
||||||
|
|
||||||
|
console.log('[AUTH] Redirecting to:', redirectUrl);
|
||||||
|
|
||||||
const responseHeaders = new Headers();
|
const responseHeaders = new Headers();
|
||||||
responseHeaders.set('Content-Type', 'application/json');
|
responseHeaders.set('Content-Type', 'application/json');
|
||||||
|
|
||||||
@ -51,7 +64,7 @@ export const POST: APIRoute = async ({ request }) => {
|
|||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
redirectTo: stateVerification.stateData.returnTo
|
redirectTo: redirectUrl
|
||||||
}), {
|
}), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: responseHeaders
|
headers: responseHeaders
|
||||||
|
@ -62,24 +62,33 @@ const currentUrl = Astro.url.href;
|
|||||||
<BaseLayout title={entry.data.title} description={entry.data.description}>
|
<BaseLayout title={entry.data.title} description={entry.data.description}>
|
||||||
{requiresAuth && (
|
{requiresAuth && (
|
||||||
<script define:vars={{ requiresAuth, articleTitle: entry.data.title }}>
|
<script define:vars={{ requiresAuth, articleTitle: entry.data.title }}>
|
||||||
// Client-side authentication check for gated content
|
// Enhanced client-side authentication check for gated content with improved error handling
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
if (!requiresAuth) return;
|
if (!requiresAuth) return;
|
||||||
|
|
||||||
console.log('[GATED CONTENT] Checking client-side auth for: ' + articleTitle);
|
console.log('[GATED CONTENT] Checking client-side auth for: ' + articleTitle);
|
||||||
|
|
||||||
|
// Check for auth success indicator in URL (from callback)
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const authSuccess = urlParams.get('auth') === 'success';
|
||||||
|
|
||||||
// Hide content immediately while checking auth
|
// Hide content immediately while checking auth
|
||||||
const contentArea = document.querySelector('.article-content');
|
const contentArea = document.querySelector('.article-content');
|
||||||
const sidebar = document.querySelector('.article-sidebar');
|
const sidebar = document.querySelector('.article-sidebar');
|
||||||
|
|
||||||
|
|
||||||
if (contentArea) {
|
if (contentArea) {
|
||||||
contentArea.style.display = 'none';
|
contentArea.style.display = 'none';
|
||||||
}
|
}
|
||||||
// DON'T hide the sidebar container - just prevent TOC generation
|
|
||||||
//if (sidebar) {
|
// If this is a redirect from successful auth, wait a bit for session to be available
|
||||||
//sidebar.innerHTML = ''; // Clear any content instead of hiding
|
if (authSuccess) {
|
||||||
//}
|
console.log('[GATED CONTENT] Auth success detected, waiting for session...');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
// Clean the URL
|
||||||
|
const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
||||||
|
window.history.replaceState({}, document.title, cleanUrl);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/auth/status');
|
const response = await fetch('/api/auth/status');
|
||||||
@ -93,7 +102,7 @@ const currentUrl = Astro.url.href;
|
|||||||
if (authRequired && !isAuthenticated) {
|
if (authRequired && !isAuthenticated) {
|
||||||
console.log('[GATED CONTENT] Access denied - showing auth required message: ' + articleTitle);
|
console.log('[GATED CONTENT] Access denied - showing auth required message: ' + articleTitle);
|
||||||
|
|
||||||
// Show authentication required message (no auto-redirect)
|
// Show authentication required message
|
||||||
if (contentArea) {
|
if (contentArea) {
|
||||||
const loginUrl = '/api/auth/login?returnTo=' + encodeURIComponent(window.location.href);
|
const loginUrl = '/api/auth/login?returnTo=' + encodeURIComponent(window.location.href);
|
||||||
contentArea.innerHTML = [
|
contentArea.innerHTML = [
|
||||||
@ -125,7 +134,7 @@ const currentUrl = Astro.url.href;
|
|||||||
if (contentArea) {
|
if (contentArea) {
|
||||||
contentArea.style.display = 'block';
|
contentArea.style.display = 'block';
|
||||||
}
|
}
|
||||||
// Let TOC generate normally for authenticated users
|
// Generate TOC for authenticated users
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof generateTOCContent === 'function') {
|
if (typeof generateTOCContent === 'function') {
|
||||||
generateTOCContent();
|
generateTOCContent();
|
||||||
@ -134,7 +143,7 @@ const currentUrl = Astro.url.href;
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[GATED CONTENT] Auth check failed:', error);
|
console.error('[GATED CONTENT] Auth check failed:', error);
|
||||||
// On error, show auth required message
|
// On error, show auth required message with retry option
|
||||||
if (requiresAuth && contentArea) {
|
if (requiresAuth && contentArea) {
|
||||||
const loginUrl = '/api/auth/login?returnTo=' + encodeURIComponent(window.location.href);
|
const loginUrl = '/api/auth/login?returnTo=' + encodeURIComponent(window.location.href);
|
||||||
contentArea.innerHTML = [
|
contentArea.innerHTML = [
|
||||||
@ -402,25 +411,9 @@ const currentUrl = Astro.url.href;
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateSidebarTOC() {
|
function generateSidebarTOC() {
|
||||||
// NEW: Don't generate TOC for gated content that requires auth
|
// Only generate TOC if not gated content OR user is authenticated
|
||||||
if (requiresAuth) {
|
if (requiresAuth) {
|
||||||
fetch('/api/auth/status')
|
// For gated content, TOC will be generated by the auth check script
|
||||||
.then(response => response.json())
|
|
||||||
.then(authStatus => {
|
|
||||||
const isAuthenticated = authStatus.gatedContentAuthenticated || false;
|
|
||||||
const authRequired = authStatus.gatedContentAuthRequired || false;
|
|
||||||
|
|
||||||
// Only generate TOC if user is authenticated for gated content
|
|
||||||
if (authRequired && !isAuthenticated) {
|
|
||||||
return; // Don't generate TOC
|
|
||||||
} else {
|
|
||||||
generateTOCContent(); // Generate TOC for authenticated users
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// On error, don't generate TOC for gated content
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,12 +607,13 @@ const currentUrl = Astro.url.href;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep your existing DOMContentLoaded; just ensure this is called
|
// Make generateTOCContent available globally for the auth check script
|
||||||
|
window.generateTOCContent = generateTOCContent;
|
||||||
|
|
||||||
|
// Initialize everything on page load
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// existing:
|
|
||||||
calculateReadingTime();
|
calculateReadingTime();
|
||||||
generateSidebarTOC();
|
generateSidebarTOC();
|
||||||
// new/updated:
|
|
||||||
enhanceCodeCopy();
|
enhanceCodeCopy();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user