68 lines
2.5 KiB
JavaScript
68 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Mint a login-link token and print a URL that logs you in as a given user.
|
|
*
|
|
* For support/debugging only. The session it creates is stamped
|
|
* `impersonatedBy: 'login-link'` so it's auditable.
|
|
*
|
|
* Config (env or edit the constants below):
|
|
* BETTER_AUTH_SECRET HS256 signing secret for the target environment
|
|
* APP_URL Base URL of the app, e.g. https://app.example.com
|
|
*
|
|
* Usage:
|
|
* BETTER_AUTH_SECRET=... APP_URL=https://app.example.com \
|
|
* node scripts/impersonate.mjs <user-uuid> <user-email> [redirect-path] [ttl]
|
|
*
|
|
* Examples:
|
|
* node scripts/impersonate.mjs 3f2a... [email protected]
|
|
* node scripts/impersonate.mjs 3f2a... [email protected] /courses 30m
|
|
*/
|
|
import { SignJWT } from 'jose';
|
|
|
|
// Load a .env file if present. Override the path with ENV_FILE=... if needed.
|
|
// Existing process.env values are NOT overwritten by the file.
|
|
try {
|
|
process.loadEnvFile(process.env.ENV_FILE ?? new URL('../.env', import.meta.url));
|
|
} catch {
|
|
// no .env found — fall back to real env vars / the constants below
|
|
}
|
|
|
|
// --- config (env wins; otherwise fill these in) -----------------------------
|
|
const BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET ?? '';
|
|
const APP_URL = process.env.APP_URL ?? 'https://app.classroomio.com';
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const [userId, email, redirect = '/', ttl = '10m'] = process.argv.slice(2);
|
|
|
|
function fail(message) {
|
|
console.error(`\n ✗ ${message}\n`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!BETTER_AUTH_SECRET) fail('Set BETTER_AUTH_SECRET (env var or the constant in this file).');
|
|
if (!APP_URL) fail('Set APP_URL (env var or the constant in this file), e.g. https://app.example.com');
|
|
if (!userId || !email) {
|
|
fail('Usage: node scripts/impersonate.mjs <user-uuid> <user-email> [redirect-path] [ttl]');
|
|
}
|
|
if (!redirect.startsWith('/')) fail('redirect-path must start with "/" (same-site only).');
|
|
|
|
const secret = new TextEncoder().encode(BETTER_AUTH_SECRET.trim());
|
|
|
|
const token = await new SignJWT({
|
|
sub: userId,
|
|
email: email.toLowerCase().trim(),
|
|
type: 'login-link'
|
|
})
|
|
.setProtectedHeader({ alg: 'HS256' })
|
|
.setIssuedAt()
|
|
.setExpirationTime(ttl)
|
|
.sign(secret);
|
|
|
|
const base = APP_URL.replace(/\/+$/, '');
|
|
const url = `${base}/api/auth/login-link?token=${encodeURIComponent(token)}&redirect=${encodeURIComponent(redirect)}`;
|
|
|
|
console.log(`\n Login as: ${email} (${userId})`);
|
|
console.log(` Expires in: ${ttl}\n`);
|
|
console.log(url);
|
|
console.log('\n Open this URL in a fresh/incognito window to start the session.\n');
|