| 61 | }; |
| 62 | |
| 63 | export class UserAuthenticator implements Authenticator { |
| 64 | authmode: string; |
| 65 | constructor(private credentials: AuthenticatorCredentials[]) { |
| 66 | this.authmode = "UserAuthenticator"; |
| 67 | } |
| 68 | |
| 69 | async checkCredentials(r: Request): Promise<AuthenticatorCheckCredentialsResponse> { |
| 70 | const res = stripUsernamePasswordFromHeader(r); |
| 71 | if ("verified" in res) { |
| 72 | return res; |
| 73 | } |
| 74 | |
| 75 | const [username, password] = res; |
| 76 | |
| 77 | const credential = this.credentials.find((c) => c.username === username); |
| 78 | if (!credential) { |
| 79 | return { verified: false, payload: null }; |
| 80 | } |
| 81 | |
| 82 | try { |
| 83 | if (!crypto.subtle.timingSafeEqual(stringToArrayBuffer(username), stringToArrayBuffer(credential.username))) { |
| 84 | return { verified: false, payload: null }; |
| 85 | } |
| 86 | |
| 87 | if (!crypto.subtle.timingSafeEqual(stringToArrayBuffer(password), stringToArrayBuffer(credential.password))) { |
| 88 | return { verified: false, payload: null }; |
| 89 | } |
| 90 | } catch (err) { |
| 91 | console.error(`Failed authentication timingSafeEqual: ${errorString(err)}`); |
| 92 | return { verified: false, payload: null }; |
| 93 | } |
| 94 | |
| 95 | const payload = { |
| 96 | username, |
| 97 | capabilities: credential.capabilities, |
| 98 | exp: Date.now() + 60 * 60, |
| 99 | aud: "", |
| 100 | }; |
| 101 | |
| 102 | return RegistryTokens.verifyPayload(r, payload); |
| 103 | } |
| 104 | } |
nothing calls this directly
no outgoing calls
no test coverage detected