UNPKG

kaven-utils

Version:

Utils for Node.js.

117 lines (116 loc) 4.81 kB
/******************************************************************** * @author: Kaven * @email: kaven@wuwenkai.com * @website: http://blog.kaven.xyz * @file: [Kaven-Utils] /src/net/authentication/KavenDigestAuthentication.ts * @create: 2019-03-26 14:22:50.325 * @modify: 2023-12-07 10:59:05.566 * @version: 5.4.0 * @times: 44 * @lines: 143 * @copyright: Copyright © 2019-2023 Kaven. All Rights Reserved. * @description: [description] * @license: [license] ********************************************************************/ import { GenerateGuid, GetMD5, KavenMD5, TrimAll } from "kaven-basic"; import { KavenAuthentication } from "./KavenAuthentication.js"; import { InternalLogger } from "../../KavenUtility.Internal.js"; export class KavenDigestAuthentication extends KavenAuthentication { /** * A server-specified string which should be uniquely generated each * time a 401 response is made. It is advised that this string be * Base64 or hexadecimal data. Specifically, since the string is * passed in the header field lines as a quoted string, the double- * quote character is not allowed, unless suitably escaped. */ get Nonce() { return GenerateGuid(); } /** * A string of data, specified by the server, that SHOULD be returned * by the client unchanged in the Authorization header field of * subsequent requests with URIs in the same protection space. It is * RECOMMENDED that this string be Base64 or hexadecimal data. */ get Opaque() { return GenerateGuid(); } Name = "Digest"; /** * Indicates the "quality of protection" options applied to the * response by the server. * * The value "auth" indicates authentication; * * the value "auth-int" indicates authentication with integrity protection. * * The server SHOULD use the same value for the qop parameter in the response as was sent by the client in the * corresponding request. */ QOP = "auth, auth-int"; /** * Only support MD5 for now. */ Algorithm = "MD5"; // "SHA-256"; // SHA-256 seems like not supported by browsers md5; async Authenticate(req) { try { const authStr = req.authorization; if (!authStr || !authStr.startsWith(this.Name)) { return false; } const str = authStr.TrimStart(this.Name, 1).trimStart(); const json = {}; const parts = str.split(","); for (const part of parts) { const index = part.indexOf("="); if (index === -1) { return false; } const name = part.substring(0, index).trimStart(); const value = TrimAll(part.substring(index + 1), "\"", 1); json[name] = value; } const key = `${json.username}${req.ip ?? ""}`; if (!this.CanAuthenticate(key)) { return false; } if (json.username !== this.UserName) { this.AddRecord(key, false); return false; } if (this.md5 === undefined) { this.md5 = new KavenMD5(); } // the notation unq(X) means the value of the quoted-string X without the surrounding quotes and with quoting slashes removed. // A1 = unq(username) ":" unq(realm) ":" password const ha1 = await GetMD5(json.username + ":" + json.realm + ":" + this.Password); // If the qop parameter's value is "auth" or is unspecified: // A2 = Method ":" request-uri const ha2 = await GetMD5(req.method + ":" + json.uri); // If the qop value is "auth" or "auth-int": // response = <"> < KD ( H(A1), unq(nonce) // ":" nc // ":" unq(cnonce) // ":" unq(qop) // ":" H(A2) // ) <"> // ha1:nonce:nc:cnonce:qop:ha2 const response = await GetMD5([ha1, json.nonce, json.nc, json.cnonce, json.qop, ha2].join(":")); if (json.response !== response) { this.AddRecord(key, false); return false; } this.AddRecord(key, true); return true; } catch (ex) { InternalLogger()?.Warn(ex); return false; } } Update(response) { response.AddHeader(this.ResponseHeaderName, `${this.Name} realm="${this.Realm}", qop="${this.QOP}", algorithm=${this.Algorithm}, nonce="${this.Nonce}", opaque="${this.Opaque}"`); return response; } }