UNPKG

kaven-utils

Version:

Utils for Node.js.

158 lines (157 loc) 5.99 kB
/******************************************************************** * @author: Kaven * @email: kaven@wuwenkai.com * @website: http://blog.kaven.xyz * @file: [Kaven-Utils] /src/net/sso/KavenSSOClient.ts * @create: 2019-02-21 11:27:41.261 * @modify: 2024-11-01 10:48:07.307 * @version: 5.4.5 * @times: 79 * @lines: 215 * @copyright: Copyright © 2019-2024 Kaven. All Rights Reserved. * @description: [description] * @license: [license] ********************************************************************/ import { DecodeByRFC3986, HttpStatusCode, RemoveQueryFromURL, ParseQueryParameters, Strings_Empty } from "kaven-basic"; import { ApiRequestEx } from "../../ApiRequestEx.js"; import { HttpGet } from "../../KavenUtility.Net.js"; import { KavenSSO, SSOAction, SSOVerifyError } from "./KavenSSO.js"; import { InternalLogger } from "../../KavenUtility.Internal.js"; export class KavenSSOClient extends KavenSSO { // #region Private Fields tokenSet = new Set(); // #endregion // #region Readonly Fields AppID = Strings_Empty; AppHost = Strings_Empty; AppNotifyPath = "/sso/notify"; ServerHost = Strings_Empty; ServerLoginPath = "/login"; ServerSSOPath = "/sso"; // #endregion // #region Public Fields // #endregion // #region Properties get ServerLoginURL() { return this.ServerHost + this.ServerLoginPath; } get ServerSSOURL() { return this.ServerHost + this.ServerSSOPath; } get AppNotifyURL() { return this.AppHost + this.AppNotifyPath; } // #endregion // #region C'tor constructor(appID, appHost, serverHost, secret, options) { super(secret, options?.parameterName, options?.tokenName); this.AppID = appID; this.AppHost = appHost; this.ServerHost = serverHost; if (options?.serverLoginPath) { this.ServerLoginPath = options.serverLoginPath; } if (options?.serverSSOPath) { this.ServerSSOPath = options.serverSSOPath; } } // #endregion // #region Public Methods MakeSSOURL(action, client, data) { const url = new ApiRequestEx(this.ServerSSOURL) .AddParameter(this.ParameterName.Action, action) .AddParameter(this.ParameterName.AppID, this.AppID) .AddParameter(this.ParameterName.NotifyTo, this.AppNotifyURL) .AddParameter(this.ParameterName.SessionID, client.sessionID) .AddParameter(this.ParameterName.Token, client.token) .AddParameter(this.ParameterName.Data, data) .MakeByHMAC_SHA1(this.Secret); return url; } MakeRedirectURL(returnTo, sessionID) { returnTo = this.RemoveSSOParametersFromURL(returnTo); const url = new ApiRequestEx(this.ServerLoginURL) .AddParameter(this.ParameterName.Action, SSOAction.Login) .AddParameter(this.ParameterName.AppID, this.AppID) .AddParameter(this.ParameterName.NotifyTo, this.AppNotifyURL) .AddParameter(this.ParameterName.SessionID, sessionID) .AddParameter(this.ParameterName.ReturnTo, returnTo) .MakeByHMAC_SHA1(this.Secret); return url; } RemoveSSOParametersFromURL(url, ...others) { const result = RemoveQueryFromURL(url, ...Object.values(this.ParameterName), ApiRequestEx.Signature, ApiRequestEx.SignatureNonce, ApiRequestEx.Timestamp, ...others); return result; } GetRedirectToServerURL(originalUrl, sessionID) { const url = this.MakeRedirectURL(this.AppHost + originalUrl, sessionID); return url; } async Verify(originalUrl) { // verify request query const apiResult = await ApiRequestEx.VerifyByHMAC_SHA1(this.Secret, originalUrl); if (apiResult !== true) { InternalLogger()?.Warn(apiResult); return undefined; } const queryParameters = ParseQueryParameters(originalUrl, DecodeByRFC3986); const result = { parameters: queryParameters, }; const action = queryParameters[this.ParameterName.Action]; if (action) { result.action = action; } const userID = queryParameters[this.ParameterName.UserID]; if (userID) { result.userID = userID; } return result; } async VerifyClient(client) { if (this.tokenSet.has(client.token)) { return true; } const url = await this.MakeSSOURL(SSOAction.Verify, client); const response = await HttpGet(url); // handle success if (response.Status === HttpStatusCode.OK) { this.tokenSet.add(client.token); return true; } else { // delete invalid token if (response.Status === HttpStatusCode.BadRequest) { InternalLogger()?.Warn(`remove verify failed token: ${client.token}`); await this.Logout(client, false); } return SSOVerifyError.TokenInvalid; } } async GetDataFromSSOServer(url) { const r = await HttpGet(url); return r.Json; } /** * @note This method will not throw an exception */ async Logout(client, notifyServer = true) { try { if (this.tokenSet.delete(client.token)) { if (this.EnableLog) { InternalLogger()?.Info(`Logout, delete token: ${client.token}`); } } if (notifyServer) { const url = await this.MakeSSOURL(SSOAction.Logout, client); const response = await HttpGet(url); if (response.Status !== HttpStatusCode.OK) { InternalLogger()?.Warn(response); } } } catch (ex) { InternalLogger()?.Error(ex); } } }