Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions lib/debug/auth-debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2025 Fasttify LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { NextRequest } from 'next/server';
import outputs from '@/amplify_outputs.json';

/**
* Función de debugging para problemas de autenticación en producción
*/
export function debugAuthIssues(request: NextRequest) {
const isProduction = process.env.NODE_ENV === 'production';

if (!isProduction) {
return null;
}

const cookies = request.headers?.get('cookie') || '';
const userAgent = request.headers?.get('user-agent') || '';
const referer = request.headers?.get('referer') || '';

// Verificar configuración de Amplify
const amplifyConfig = {
hasAuth: !!outputs.auth,
userPoolId: outputs.auth?.user_pool_id,
region: outputs.auth?.aws_region,
clientId: outputs.auth?.user_pool_client_id,
identityPoolId: outputs.auth?.identity_pool_id,

Check failure on line 40 in lib/debug/auth-debug.ts

View workflow job for this annotation

GitHub Actions / build

Property 'identity_pool_id' does not exist on type '{ user_pool_id: string; aws_region: string; user_pool_client_id: string; }'.
oauthDomain: outputs.auth?.oauth?.domain,

Check failure on line 41 in lib/debug/auth-debug.ts

View workflow job for this annotation

GitHub Actions / build

Property 'oauth' does not exist on type '{ user_pool_id: string; aws_region: string; user_pool_client_id: string; }'.
redirectUris: outputs.auth?.oauth?.redirect_sign_in_uri,

Check failure on line 42 in lib/debug/auth-debug.ts

View workflow job for this annotation

GitHub Actions / build

Property 'oauth' does not exist on type '{ user_pool_id: string; aws_region: string; user_pool_client_id: string; }'.
};

// Verificar cookies de Cognito
const cognitoCookies = {
hasAnyCognito: cookies.includes('CognitoIdentityServiceProvider'),
hasAmplify: cookies.includes('aws-amplify'),
cookieCount: cookies.split(';').length,
cognitoPatterns: [/CognitoIdentityServiceProvider[^=]*=([^;]+)/g, /aws-amplify[^=]*=([^;]+)/g]
.map((pattern) => {
const matches = [...cookies.matchAll(pattern)];
return matches.map((match) => match[1]);
})
.flat(),
};

// Verificar headers importantes
const headers = {
host: request.headers?.get('host'),
origin: request.headers?.get('origin'),
xForwardedFor: request.headers?.get('x-forwarded-for'),
xForwardedProto: request.headers?.get('x-forwarded-proto'),
};

const debugInfo = {
timestamp: new Date().toISOString(),
path: request.nextUrl.pathname,
amplifyConfig,
cognitoCookies,
headers,
userAgent: userAgent.substring(0, 100),
referer: referer.substring(0, 100),
};

console.log('Auth Debug Info:', JSON.stringify(debugInfo, null, 2));

return debugInfo;
}

/**
* Verifica si la configuración de Amplify es válida para producción
*/
export function validateAmplifyConfig() {
const requiredFields = [
'auth.user_pool_id',
'auth.aws_region',
'auth.user_pool_client_id',
'auth.identity_pool_id',
'auth.oauth.domain',
'auth.oauth.redirect_sign_in_uri',
];

const missingFields = requiredFields.filter((field) => {
const keys = field.split('.');
let value: any = outputs;
for (const key of keys) {
value = value?.[key];
}
return !value;
});

if (missingFields.length > 0) {
console.error('Missing required Amplify config fields:', missingFields);
return false;
}

// Verificar que las URLs de redirección incluyan el dominio de producción
const redirectUris = outputs.auth?.oauth?.redirect_sign_in_uri || [];

Check failure on line 109 in lib/debug/auth-debug.ts

View workflow job for this annotation

GitHub Actions / build

Property 'oauth' does not exist on type '{ user_pool_id: string; aws_region: string; user_pool_client_id: string; }'.
const hasProductionUrl = redirectUris.some((uri) => uri.includes('fasttify.com') || uri.includes('www.fasttify.com'));

Check failure on line 110 in lib/debug/auth-debug.ts

View workflow job for this annotation

GitHub Actions / build

Parameter 'uri' implicitly has an 'any' type.
Comment thread Dismissed
Comment thread Dismissed

if (!hasProductionUrl) {
console.error('No production redirect URI found in Amplify config');
return false;
}

return true;
}
59 changes: 56 additions & 3 deletions middlewares/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { runWithAmplifyServerContext } from '@/utils/client/AmplifyUtils';
import { fetchAuthSession } from 'aws-amplify/auth/server';
import { getLastVisitedStore } from '@/lib/cookies/last-store';
import { debugAuthIssues, validateAmplifyConfig } from '@/lib/debug/auth-debug';
import { NextRequest, NextResponse } from 'next/server';
import NodeCache from 'node-cache';

Expand Down Expand Up @@ -49,7 +50,6 @@ export function clearUserSessionCache(request: NextRequest): void {
}

function getCacheKey(request: NextRequest): string {
// Usar un hash simple de las cookies principales de autenticación
const cookies = request.headers?.get('cookie') || '';

// Buscar el ID de usuario en las cookies para crear una clave estable
Expand All @@ -58,8 +58,31 @@ function getCacheKey(request: NextRequest): string {
return `user-${userIdMatch[1]}`;
}

// Fallback: usar toda la cadena de cookies como clave
return cookies ? `cookies-${cookies.length}` : 'no-auth';
// Buscar cualquier cookie de Cognito para crear una clave única
const cognitoMatch = cookies.match(/CognitoIdentityServiceProvider[^=]*=([^;]+)/);
if (cognitoMatch) {
return `cognito-${cognitoMatch[1]}`;
}

// Buscar cookies de AWS Amplify (formato alternativo)
const amplifyMatch = cookies.match(/aws-amplify[^=]*=([^;]+)/);
if (amplifyMatch) {
return `amplify-${amplifyMatch[1]}`;
}

// Si no hay cookies de Cognito, usar un hash de todas las cookies para evitar conflictos
if (cookies) {
// Crear un hash simple pero único basado en el contenido de las cookies
let hash = 0;
for (let i = 0; i < cookies.length; i++) {
const char = cookies.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convertir a 32bit integer
}
return `cookies-${Math.abs(hash)}`;
}

return 'no-auth';
}

export async function getSession(request: NextRequest, response: NextResponse, forceRefresh = true) {
Expand Down Expand Up @@ -92,6 +115,25 @@ export async function getSession(request: NextRequest, response: NextResponse, f
return result;
} catch (error) {
console.error('Error fetching user session:', error);

// En producción, ser más permisivo con errores de red/temporales
const isProduction = process.env.NODE_ENV === 'production';
const isNetworkError =
error instanceof Error &&
(error.message.includes('network') ||
error.message.includes('timeout') ||
error.message.includes('ECONNRESET') ||
error.message.includes('ENOTFOUND'));

// Si es un error de red en producción, intentar usar caché existente
if (isProduction && isNetworkError) {
const cached = sessionCache.get(cacheKey);
if (cached) {
console.log('Using cached session due to network error');
return cached;
}
}

// Limpiar caché en caso de error
sessionCache.del(cacheKey);
return null;
Expand All @@ -101,9 +143,20 @@ export async function getSession(request: NextRequest, response: NextResponse, f
}

export async function handleAuthenticationMiddleware(request: NextRequest, response: NextResponse) {
// Validar configuración de Amplify en producción
const isProduction = process.env.NODE_ENV === 'production';
if (isProduction && !validateAmplifyConfig()) {
console.error('Invalid Amplify configuration detected');
}

const session = await getSession(request, response);

if (!session) {
// Debug detallado en producción
if (isProduction) {
debugAuthIssues(request);
}

// Limpiar caché cuando no hay sesión válida
clearUserSessionCache(request);
return NextResponse.redirect(new URL('/login', request.url), { status: 302 });
Expand Down
30 changes: 11 additions & 19 deletions middlewares/store-access/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ export async function handleStoreMiddleware(request: NextRequest, response: Next
const validPlans = ['Royal', 'Majestic', 'Imperial'];

if (!userPlan || !validPlans.includes(userPlan)) {
// Usuario con plan 'free' o sin plan - verificar si tiene suscripción en DB
// Usuario con plan 'free' o sin plan - permitir acceso a /my-store para selección de planes
if (path === '/my-store') {
// Permitir acceso a /my-store para usuarios con plan free
return response;
}

// Para otras rutas, verificar si tiene suscripción en DB
try {
const { data: subscriptions } = await cookiesClient.models.UserSubscription.listUserSubscriptionByUserId({
userId,
Expand All @@ -91,27 +97,13 @@ export async function handleStoreMiddleware(request: NextRequest, response: Next
return NextResponse.redirect(new URL('/my-store', request.url), { status: 302 });
}
} else {
// No tiene suscripción - usuario nuevo
const lastStoreId = getLastVisitedStore(request);
if (lastStoreId) {
return NextResponse.redirect(new URL(`/store/${lastStoreId}/access_account/checkout`, request.url), {
status: 302,
});
} else {
return NextResponse.redirect(new URL('/my-store', request.url), { status: 302 });
}
// No tiene suscripción - redirigir a /my-store para selección de planes
return NextResponse.redirect(new URL('/my-store', request.url), { status: 302 });
}
} catch (error) {
console.error('Error checking subscription:', error);
// En caso de error, redirigir a última tienda
const lastStoreId = getLastVisitedStore(request);
if (lastStoreId) {
return NextResponse.redirect(new URL(`/store/${lastStoreId}/access_account/checkout`, request.url), {
status: 302,
});
} else {
return NextResponse.redirect(new URL('/my-store', request.url), { status: 302 });
}
// En caso de error, redirigir a /my-store
return NextResponse.redirect(new URL('/my-store', request.url), { status: 302 });
}
}

Expand Down
Loading