mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-09 12:16:36 +00:00
hash device codes
This commit is contained in:
@@ -11,6 +11,8 @@ import {
|
|||||||
createSession,
|
createSession,
|
||||||
generateSessionToken
|
generateSessionToken
|
||||||
} from "@server/auth/sessions/app";
|
} from "@server/auth/sessions/app";
|
||||||
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
|
||||||
const paramsSchema = z.object({
|
const paramsSchema = z.object({
|
||||||
code: z.string().min(1, "Code is required")
|
code: z.string().min(1, "Code is required")
|
||||||
@@ -18,6 +20,13 @@ const paramsSchema = z.object({
|
|||||||
|
|
||||||
export type PollDeviceWebAuthParams = z.infer<typeof paramsSchema>;
|
export type PollDeviceWebAuthParams = z.infer<typeof paramsSchema>;
|
||||||
|
|
||||||
|
// Helper function to hash device code before querying database
|
||||||
|
function hashDeviceCode(code: string): string {
|
||||||
|
return encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(code))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export type PollDeviceWebAuthResponse = {
|
export type PollDeviceWebAuthResponse = {
|
||||||
verified: boolean;
|
verified: boolean;
|
||||||
token?: string;
|
token?: string;
|
||||||
@@ -68,11 +77,14 @@ export async function pollDeviceWebAuth(
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const requestIp = extractIpFromRequest(req);
|
const requestIp = extractIpFromRequest(req);
|
||||||
|
|
||||||
|
// Hash the code before querying
|
||||||
|
const hashedCode = hashDeviceCode(code);
|
||||||
|
|
||||||
// Find the code in the database
|
// Find the code in the database
|
||||||
const [deviceCode] = await db
|
const [deviceCode] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(deviceWebAuthCodes)
|
.from(deviceWebAuthCodes)
|
||||||
.where(eq(deviceWebAuthCodes.code, code))
|
.where(eq(deviceWebAuthCodes.code, hashedCode))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (!deviceCode) {
|
if (!deviceCode) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { alphabet, generateRandomString } from "oslo/crypto";
|
|||||||
import { createDate } from "oslo";
|
import { createDate } from "oslo";
|
||||||
import { TimeSpan } from "oslo";
|
import { TimeSpan } from "oslo";
|
||||||
import { maxmindLookup } from "@server/db/maxmind";
|
import { maxmindLookup } from "@server/db/maxmind";
|
||||||
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
deviceName: z.string().optional(),
|
deviceName: z.string().optional(),
|
||||||
@@ -30,6 +32,13 @@ function generateDeviceCode(): string {
|
|||||||
return `${part1}-${part2}`;
|
return `${part1}-${part2}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to hash device code before storing in database
|
||||||
|
function hashDeviceCode(code: string): string {
|
||||||
|
return encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(code))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to extract IP from request
|
// Helper function to extract IP from request
|
||||||
function extractIpFromRequest(req: Request): string | undefined {
|
function extractIpFromRequest(req: Request): string | undefined {
|
||||||
const ip = req.ip || req.socket.remoteAddress;
|
const ip = req.ip || req.socket.remoteAddress;
|
||||||
@@ -99,6 +108,9 @@ export async function startDeviceWebAuth(
|
|||||||
// Generate device code
|
// Generate device code
|
||||||
const code = generateDeviceCode();
|
const code = generateDeviceCode();
|
||||||
|
|
||||||
|
// Hash the code before storing in database
|
||||||
|
const hashedCode = hashDeviceCode(code);
|
||||||
|
|
||||||
// Extract IP from request
|
// Extract IP from request
|
||||||
const ip = extractIpFromRequest(req);
|
const ip = extractIpFromRequest(req);
|
||||||
|
|
||||||
@@ -108,9 +120,9 @@ export async function startDeviceWebAuth(
|
|||||||
// Set expiration to 5 minutes from now
|
// Set expiration to 5 minutes from now
|
||||||
const expiresAt = createDate(new TimeSpan(5, "m")).getTime();
|
const expiresAt = createDate(new TimeSpan(5, "m")).getTime();
|
||||||
|
|
||||||
// Insert into database
|
// Insert into database (store hashed code)
|
||||||
await db.insert(deviceWebAuthCodes).values({
|
await db.insert(deviceWebAuthCodes).values({
|
||||||
code,
|
code: hashedCode,
|
||||||
ip: ip || null,
|
ip: ip || null,
|
||||||
city: city || null,
|
city: city || null,
|
||||||
deviceName: deviceName || null,
|
deviceName: deviceName || null,
|
||||||
|
|||||||
@@ -7,12 +7,21 @@ import logger from "@server/logger";
|
|||||||
import { response } from "@server/lib/response";
|
import { response } from "@server/lib/response";
|
||||||
import { db, deviceWebAuthCodes } from "@server/db";
|
import { db, deviceWebAuthCodes } from "@server/db";
|
||||||
import { eq, and, gt } from "drizzle-orm";
|
import { eq, and, gt } from "drizzle-orm";
|
||||||
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
code: z.string().min(1, "Code is required"),
|
code: z.string().min(1, "Code is required"),
|
||||||
verify: z.boolean().optional().default(false) // If false, just check and return metadata
|
verify: z.boolean().optional().default(false) // If false, just check and return metadata
|
||||||
}).strict();
|
}).strict();
|
||||||
|
|
||||||
|
// Helper function to hash device code before querying database
|
||||||
|
function hashDeviceCode(code: string): string {
|
||||||
|
return encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(code))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export type VerifyDeviceWebAuthBody = z.infer<typeof bodySchema>;
|
export type VerifyDeviceWebAuthBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
export type VerifyDeviceWebAuthResponse = {
|
export type VerifyDeviceWebAuthResponse = {
|
||||||
@@ -49,13 +58,16 @@ export async function verifyDeviceWebAuth(
|
|||||||
|
|
||||||
logger.debug("Verifying device web auth code:", { code });
|
logger.debug("Verifying device web auth code:", { code });
|
||||||
|
|
||||||
|
// Hash the code before querying
|
||||||
|
const hashedCode = hashDeviceCode(code);
|
||||||
|
|
||||||
// Find the code in the database that is not expired and not already verified
|
// Find the code in the database that is not expired and not already verified
|
||||||
const [deviceCode] = await db
|
const [deviceCode] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(deviceWebAuthCodes)
|
.from(deviceWebAuthCodes)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(deviceWebAuthCodes.code, code),
|
eq(deviceWebAuthCodes.code, hashedCode),
|
||||||
gt(deviceWebAuthCodes.expiresAt, now),
|
gt(deviceWebAuthCodes.expiresAt, now),
|
||||||
eq(deviceWebAuthCodes.verified, false)
|
eq(deviceWebAuthCodes.verified, false)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user