start login, add support for authorization header jwt
This commit is contained in:
parent
221af7d0c1
commit
f2e8b301e9
@ -48,17 +48,18 @@ export default {
|
||||
decode(jwt: string | JWTPayload, key?: string) {
|
||||
if (typeof jwt === 'object') return jwt;
|
||||
const segments = jwt.split('.');
|
||||
if (segments.length != 3) throw new Error("Expected jwt to have exactly 2 dots.");
|
||||
if (segments.length != 3) throw new Error("Expected JWT to have exactly 2 dots.");
|
||||
const [ rawHeader, rawPayload, givenSig ] = segments;
|
||||
const data = rawHeader + '.' + rawPayload;
|
||||
|
||||
if (key != undefined) {
|
||||
if (JSON.parse(fromBase64(rawHeader))?.alg != "HS256") return undefined;
|
||||
if (JSON.parse(fromBase64(rawHeader))?.alg != "HS256") throw new Error("Invalid JWT algorithm.");
|
||||
const actualSig = trimBase64(hmac('sha256', key, data, 'utf8', 'base64')) as string;
|
||||
if (givenSig != actualSig) return undefined;
|
||||
if (givenSig != actualSig) throw new Error("Invalid JWT signature.");
|
||||
}
|
||||
|
||||
return JSON.parse(fromBase64(rawPayload)) as JWTPayload;
|
||||
try { return JSON.parse(fromBase64(rawPayload)) as JWTPayload; }
|
||||
catch { throw new Error("Invalid JWT payload."); }
|
||||
},
|
||||
validate(j: string | JWTPayload, key: string) {
|
||||
if (typeof j === 'object') {
|
||||
|
@ -6,7 +6,11 @@ import HttpError from "../server/HttpError.ts";
|
||||
import User from "../models/User.ts";
|
||||
import AppRouter from "./AppRouter.ts";
|
||||
|
||||
interface SignupRequest {
|
||||
export interface SignupRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
@ -20,10 +24,12 @@ export default class UserRouter extends AppRouter {
|
||||
|
||||
return { username: res.username };
|
||||
}
|
||||
|
||||
@rest('POST', '/signup')
|
||||
async signup(
|
||||
@schema({
|
||||
username: 'string', password: 'string',
|
||||
username: 'string',
|
||||
password: 'string',
|
||||
}) @body() body: SignupRequest
|
||||
) {
|
||||
if (await this.users.countDocuments({ username: body.username }) > 0) {
|
||||
@ -39,6 +45,18 @@ export default class UserRouter extends AppRouter {
|
||||
|
||||
return {};
|
||||
}
|
||||
@rest('POST', '/login')
|
||||
async login(
|
||||
@schema({
|
||||
username: 'string',
|
||||
password: 'string'
|
||||
}) @body() body: LoginRequest
|
||||
) {
|
||||
const res = await this.users.findOne({ username: body.username });
|
||||
if (!res) throw new HttpError('Incorrect username or password.');
|
||||
bcrypt.hash(res.password, this.salt);
|
||||
|
||||
}
|
||||
|
||||
public constructor(private salt: string, private users: Collection<User>) {
|
||||
super();
|
||||
|
@ -13,7 +13,8 @@ export interface RestOptions {
|
||||
route?: string;
|
||||
}
|
||||
|
||||
export type ProcessFunc<T extends Router> = (this: T, req: RestRequest, arg: unknown, name: string) => Promise<unknown> | unknown;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export type ProcessFunc<T extends Router> = (this: T, req: RestRequest, arg: any, name: string) => Promise<unknown> | unknown;
|
||||
|
||||
export interface RouterHandler {
|
||||
path: string;
|
||||
@ -27,7 +28,7 @@ export function addMetaQuery<T extends Router>(target: T, ...handlers: ((r: T) =
|
||||
}
|
||||
|
||||
export function makeParameterModifier<T extends Router>(func: ProcessFunc<T>) {
|
||||
return (target: T, key: string & keyof T, index: number) => {
|
||||
return (target: T, key: string, index: number) => {
|
||||
let res = Reflect.getOwnMetadata('router:params', target, key);
|
||||
|
||||
if (res === undefined) Reflect.defineMetadata('router:params', res = [], target, key);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import body from "./decorators/body.ts";
|
||||
import rest from "./decorators/rest.ts";
|
||||
import auth from "./decorators/auth.ts";
|
||||
import route from "./decorators/route.ts";
|
||||
import schema from "./decorators/schema.ts";
|
||||
|
||||
export { body, schema, rest, route };
|
||||
export { body, schema, rest, route, auth };
|
@ -1,16 +1,10 @@
|
||||
import JWT from "../../JWT.ts";
|
||||
import Router, { makeParameterModifier } from "../Router.ts";
|
||||
import { makeParameterModifier } from "../Router.ts";
|
||||
|
||||
export type AuthType = 'raw' | 'jwt';
|
||||
|
||||
export function auth<T extends Router>(salt: (self: T) => string, type: AuthType = 'jwt') {
|
||||
return makeParameterModifier<T>(function (req) {
|
||||
let res = req.headers.authorization;
|
||||
export default function auth() {
|
||||
return makeParameterModifier(function (req) {
|
||||
const res = req.headers.authorization;
|
||||
if (typeof res !== 'string') return undefined;
|
||||
if (res.startsWith('Bearer')) res = res.substring(6).trimStart();
|
||||
|
||||
if (type === 'jwt') return JWT.decode(res, salt(this));
|
||||
|
||||
return res;
|
||||
if (res.startsWith('Bearer')) return res.substring(6).trimStart();
|
||||
else return undefined;
|
||||
});
|
||||
}
|
10
src/server/decorators/jwt.ts
Normal file
10
src/server/decorators/jwt.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import JWT from "../../JWT.ts";
|
||||
import Router, { makeParameterModifier } from "../Router.ts";
|
||||
|
||||
export default function jwt<T extends Router>(salt: ((self: T) => string) | keyof T) {
|
||||
return makeParameterModifier<T>(function (_req, val?: string) {
|
||||
if (val === undefined) return undefined;
|
||||
const s = typeof salt === 'function' ? salt(this) : this[salt] as string;
|
||||
return JWT.decode(val, s);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user