(token: string)
| 47 | | { valid: false } |
| 48 | |
| 49 | export function verifyUploadToken(token: string): UploadTokenVerification { |
| 50 | if (typeof token !== 'string') { |
| 51 | return { valid: false } |
| 52 | } |
| 53 | const parts = token.split('.') |
| 54 | if (parts.length !== 2) return { valid: false } |
| 55 | const [encoded, signature] = parts |
| 56 | if (!encoded || !signature) return { valid: false } |
| 57 | |
| 58 | const expected = sign(encoded) |
| 59 | if (!safeCompare(signature, expected)) { |
| 60 | return { valid: false } |
| 61 | } |
| 62 | |
| 63 | let parsed: SignedPayload |
| 64 | try { |
| 65 | parsed = JSON.parse(fromBase64Url(encoded)) as SignedPayload |
| 66 | } catch { |
| 67 | return { valid: false } |
| 68 | } |
| 69 | |
| 70 | if ( |
| 71 | parsed.v !== 1 || |
| 72 | typeof parsed.exp !== 'number' || |
| 73 | parsed.exp < Math.floor(Date.now() / 1000) || |
| 74 | typeof parsed.uploadId !== 'string' || |
| 75 | typeof parsed.key !== 'string' || |
| 76 | typeof parsed.userId !== 'string' || |
| 77 | typeof parsed.workspaceId !== 'string' || |
| 78 | typeof parsed.context !== 'string' |
| 79 | ) { |
| 80 | return { valid: false } |
| 81 | } |
| 82 | |
| 83 | return { |
| 84 | valid: true, |
| 85 | payload: { |
| 86 | uploadId: parsed.uploadId, |
| 87 | key: parsed.key, |
| 88 | userId: parsed.userId, |
| 89 | workspaceId: parsed.workspaceId, |
| 90 | context: parsed.context as StorageContext, |
| 91 | ...(typeof parsed.fileName === 'string' ? { fileName: parsed.fileName } : {}), |
| 92 | ...(typeof parsed.contentType === 'string' ? { contentType: parsed.contentType } : {}), |
| 93 | ...(typeof parsed.fileSize === 'number' ? { fileSize: parsed.fileSize } : {}), |
| 94 | }, |
| 95 | } |
| 96 | } |
no test coverage detected