@@ -2,29 +2,85 @@ import { createError, eventHandler, readBody } from 'h3'
22import { z } from 'zod'
33import { sign } from 'jsonwebtoken'
44
5- const refreshTokens : Record < number , Record < string , any > > = { }
5+ /*
6+ * DISCLAIMER!
7+ * This is a demo implementation, please create your own handlers
8+ */
9+
10+ /**
11+ * This is a demo secret.
12+ * Please ensure that your secret is properly protected.
13+ */
614export const SECRET = 'dummy'
715
16+ /** 30 seconds */
17+ export const ACCESS_TOKEN_TTL = 30
18+
19+ export interface User {
20+ username : string
21+ name : string
22+ picture : string
23+ }
24+
25+ export interface JwtPayload extends User {
26+ scope : Array < 'test' | 'user' >
27+ exp ?: number
28+ }
29+
30+ interface TokensByUser {
31+ access : Map < string , string >
32+ refresh : Map < string , string >
33+ }
34+
35+ /**
36+ * Tokens storage.
37+ * You will need to implement your own, connect with DB/etc.
38+ */
39+ export const tokensByUser : Map < string , TokensByUser > = new Map ( )
40+
41+ /**
42+ * We use a fixed password for demo purposes.
43+ * You can use any implementation fitting your usecase.
44+ */
45+ const credentialsSchema = z . object ( {
46+ username : z . string ( ) . min ( 1 ) ,
47+ password : z . literal ( 'hunter2' )
48+ } )
49+
850export default eventHandler ( async ( event ) => {
9- const result = z . object ( { username : z . string ( ) . min ( 1 ) , password : z . literal ( 'hunter2' ) } ) . safeParse ( await readBody ( event ) )
51+ const result = credentialsSchema . safeParse ( await readBody ( event ) )
1052 if ( ! result . success ) {
11- throw createError ( { statusCode : 403 , statusMessage : 'Unauthorized, hint: try `hunter2` as password' } )
53+ throw createError ( {
54+ statusCode : 403 ,
55+ statusMessage : 'Unauthorized, hint: try `hunter2` as password'
56+ } )
1257 }
1358
14- const expiresIn = 15
15- const refreshToken = Math . floor ( Math . random ( ) * ( 1000000000000000 - 1 + 1 ) ) + 1
59+ // Emulate login
1660 const { username } = result . data
1761 const user = {
1862 username,
1963 picture : 'https://github.com/nuxt.png' ,
2064 name : `User ${ username } `
2165 }
2266
23- const accessToken = sign ( { ...user , scope : [ 'test' , 'user' ] } , SECRET , { expiresIn } )
24- refreshTokens [ refreshToken ] = {
25- accessToken,
26- user
67+ const tokenData : JwtPayload = { ...user , scope : [ 'test' , 'user' ] }
68+ const accessToken = sign ( tokenData , SECRET , {
69+ expiresIn : ACCESS_TOKEN_TTL
70+ } )
71+ const refreshToken = sign ( tokenData , SECRET , {
72+ // 1 day
73+ expiresIn : 60 * 60 * 24
74+ } )
75+
76+ // Naive implementation - please implement properly yourself!
77+ const userTokens : TokensByUser = tokensByUser . get ( username ) ?? {
78+ access : new Map ( ) ,
79+ refresh : new Map ( )
2780 }
81+ userTokens . access . set ( accessToken , refreshToken )
82+ userTokens . refresh . set ( refreshToken , accessToken )
83+ tokensByUser . set ( username , userTokens )
2884
2985 return {
3086 token : {
@@ -33,3 +89,9 @@ export default eventHandler(async (event) => {
3389 }
3490 }
3591} )
92+
93+ export function extractToken ( authorizationHeader : string ) {
94+ return authorizationHeader . startsWith ( 'Bearer ' )
95+ ? authorizationHeader . slice ( 7 )
96+ : authorizationHeader
97+ }
0 commit comments