| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- import { client } from '$lib/redis.js';
- import { getSqlite3 } from '$lib/sqlite3/database.js';
- /**
- * Authenticate a client using client_secret_basic or client_secret_post.
- */
- async function authenticateClient(request, body) {
- const auth = request.headers.get('authorization');
- let clientId, clientSecret;
- if (auth && auth.startsWith('Basic ')) {
- const decoded = Buffer.from(auth.slice(6), 'base64').toString();
- const idx = decoded.indexOf(':');
- clientId = decoded.slice(0, idx);
- clientSecret = decoded.slice(idx + 1);
- } else {
- clientId = body.client_id;
- clientSecret = body.client_secret;
- }
- if (!clientId) return null;
- const db = await getSqlite3();
- const row = db.prepare('SELECT * FROM clients WHERE client_id = ?').get(clientId);
- if (!row) return null;
- // Public clients (token_endpoint_auth_method = "none") don't require a secret
- if (row.token_endpoint_auth_method !== 'none' && row.client_secret !== clientSecret) return null;
- return row;
- }
- export async function POST({ request }) {
- let body;
- try {
- body = Object.fromEntries(await request.formData());
- } catch {
- return new Response(
- JSON.stringify({ error: 'invalid_request', error_description: 'Invalid form body' }),
- { status: 400, headers: { 'Content-Type': 'application/json' } }
- );
- }
- const clientRow = await authenticateClient(request, body);
- if (!clientRow) {
- return new Response(
- JSON.stringify({ error: 'invalid_client', error_description: 'Client authentication failed' }),
- { status: 401, headers: { 'Content-Type': 'application/json', 'WWW-Authenticate': 'Basic' } }
- );
- }
- const token = body.token;
- if (!token) {
- return new Response(
- JSON.stringify({ error: 'invalid_request', error_description: 'token is required' }),
- { status: 400, headers: { 'Content-Type': 'application/json' } }
- );
- }
- let value = await client.get(`access_token:${token}`);
- let tokenType = 'access_token';
- if (!value) {
- value = await client.get(`refresh_token:${token}`);
- tokenType = 'refresh_token';
- }
- if (!value) {
- return new Response(
- JSON.stringify({ active: false }),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
- );
- }
- let data;
- try {
- data = JSON.parse(value);
- } catch {
- return new Response(
- JSON.stringify({ active: false }),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
- );
- }
- const now = Math.floor(Date.now() / 1000);
- let exp;
- if (data.expires_at) {
- exp = Math.floor(new Date(data.expires_at).getTime() / 1000);
- if (now > exp) {
- return new Response(
- JSON.stringify({ active: false }),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
- );
- }
- } else {
- const ttl = await client.ttl(`${tokenType}:${token}`);
- if (ttl === -2) {
- return new Response(
- JSON.stringify({ active: false }),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
- );
- }
- if (ttl > 0) {
- exp = now + ttl;
- }
- }
- const response = {
- active: true,
- client_id: data.client_id,
- scope: data.scope,
- token_type: tokenType
- };
- if (typeof exp === 'number') {
- response.exp = exp;
- }
- return new Response(
- JSON.stringify(response),
- { status: 200, headers: { 'Content-Type': 'application/json' } }
- );
- }
|