diff --git a/packages/base/package.json b/packages/base/package.json index c6c5f65..dfabde2 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -1,6 +1,6 @@ { "name": "@auth-tools/base", - "version": "0.0.1-alpha.4", + "version": "0.0.1-alpha.8", "description": "A structured authentication protocol for Javascript. (base)", "main": "dist/index.js", "repository": "https://github.com/auth-tools/auth-tools", diff --git a/packages/base/src/events/intercept.ts b/packages/base/src/events/intercept.ts index d7606a3..24de94f 100644 --- a/packages/base/src/events/intercept.ts +++ b/packages/base/src/events/intercept.ts @@ -14,7 +14,7 @@ export type InterceptEventsDefinition = { type InterceptEvent = { data: ClassInterceptEvents["data"]; return: { - serverError?: boolean; + error?: boolean; intercepted: boolean; interceptCode: number; }; diff --git a/packages/base/src/events/use.ts b/packages/base/src/events/use.ts index 5831a36..fe45780 100644 --- a/packages/base/src/events/use.ts +++ b/packages/base/src/events/use.ts @@ -14,7 +14,7 @@ export type UseEventsDefinition = { //data for an use event type UseEvent = { data: ClassUseEvents["data"]; - return: { serverError?: boolean } & ClassUseEvents["return"]; + return: { error?: boolean } & ClassUseEvents["return"]; }; //all use events diff --git a/packages/base/src/protocol/index.ts b/packages/base/src/protocol/index.ts index 0600909..40bef2d 100644 --- a/packages/base/src/protocol/index.ts +++ b/packages/base/src/protocol/index.ts @@ -83,7 +83,6 @@ export type AuthProtocol = { validate_1: AuthResponseBuilder<"validate", 1, null>; validate_2: AuthResponseBuilder<"validate", 2, null>; validate_3: AuthResponseBuilder<"validate", 3, null>; - validate_9: AuthResponseBuilder<"validate", 9, null, number>; } >; register: AuthMethodBuilder< diff --git a/packages/base/src/protocol/messages.ts b/packages/base/src/protocol/messages.ts index 4b07d01..775414a 100644 --- a/packages/base/src/protocol/messages.ts +++ b/packages/base/src/protocol/messages.ts @@ -5,7 +5,6 @@ export type AuthMessages = { validate_1: "The validation method is disabled."; validate_2: 'The "accessToken" is missing.'; validate_3: 'The "accessToken" is invalid.'; - validate_9: "The validation request was intercepted."; register_0: "Registration successful."; register_1: "The registration method is disabled."; register_2: 'The "email", "username" or "password" is missing.'; diff --git a/packages/client/package.json b/packages/client/package.json index 703c07b..e2123a5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@auth-tools/client", - "version": "0.0.0", + "version": "0.0.1-alpha.3", "description": "A structured authentication protocol for Javascript. (client)", "main": "dist/index.js", "repository": "https://github.com/auth-tools/auth-tools", @@ -14,9 +14,12 @@ "prepublish": "npm run build", "remove": "npm run build:remove && rimraf node_modules yarn.lock package-lock.json pnpm-lock.yaml" }, - "dependencies": {}, + "dependencies": { + "@auth-tools/base": "^0.0.1-alpha.8" + }, "devDependencies": { "@auth-tools/client": "link:.", + "@auth-tools/logger": "^0.0.1-alpha.1", "rimraf": "^5.0.5", "ts-node-dev": "^2.0.0", "typescript": "^5.4.5" diff --git a/packages/client/src/auth.ts b/packages/client/src/auth.ts new file mode 100644 index 0000000..625fa9d --- /dev/null +++ b/packages/client/src/auth.ts @@ -0,0 +1,92 @@ +import { + AuthBase, + AuthProtocol, + AuthRequest, + AuthResponse, + UseEventCallbacks, + User, +} from "@auth-tools/base"; +import { LogFunction } from "@auth-tools/logger"; +import { undefinedUseEvent } from "./events"; +import { createValidate } from "./methods/validate"; +import { createRegister } from "./methods/register"; +import { createLogin } from "./methods/login"; +import { createLogout } from "./methods/logout"; +import { createRefresh } from "./methods/refresh"; +import { createCheck } from "./methods/check"; + +type MethodReturn = + | { clientError: true; res: null } + | { clientError: false; res: AuthResponse }; + +export type AuthClientConnector = ( + method: MethodName, + data: AuthRequest +) => MethodReturn; + +type TokenTypes = "accessToken" | "refreshToken"; + +//config passed by user to class +export type AuthClientConfig = { + connector: AuthClientConnector; +}; + +//all use events +export type AuthClientUseEvents = { + getToken: { + data: { type: TokenTypes }; + return: { token: User[TokenTypes] | null }; + }; + storeToken: { + data: { type: TokenTypes; token: User[TokenTypes] }; + return: {}; + }; + deleteToken: { + data: { type: TokenTypes }; + return: {}; + }; +}; + +export type AuthClientMethod< + MethodName extends keyof AuthProtocol, + NoData extends boolean = MethodName extends "logout" | "refresh" | "check" + ? true + : false +> = ( + data: NoData extends true ? {} : AuthRequest +) => Promise>; + +type AuthClientMethods = { + [MethodName in keyof AuthProtocol]: AuthClientMethod; +}; + +export class AuthClient extends AuthBase< + AuthClientConfig, + AuthClientUseEvents, + {} +> { + //all methods + public methods: AuthClientMethods; + + constructor(config: AuthClientConfig, log: LogFunction) { + //defaults for use event callbacks + const defaultedUseEvents: UseEventCallbacks = { + getToken: undefinedUseEvent("getToken", { token: "" }, log), + storeToken: undefinedUseEvent("storeToken", {}, log), + deleteToken: undefinedUseEvent("deleteToken", {}, log), + }; + + //init authbase class + super(config, log, defaultedUseEvents, {}); + + //all auth methods + this.methods = { + validate: createValidate(this._internal), + register: createRegister(this._internal), + login: createLogin(this._internal), + logout: createLogout(this._internal), + refresh: createRefresh(this._internal), + check: createCheck(this._internal), + }; + } +} diff --git a/packages/client/src/events.ts b/packages/client/src/events.ts new file mode 100644 index 0000000..58a68ea --- /dev/null +++ b/packages/client/src/events.ts @@ -0,0 +1,19 @@ +import { UseEventCallbacks } from "@auth-tools/base"; +import { AuthClientUseEvents } from "./auth"; +import { LogFunction } from "@auth-tools/logger"; + +//for an undefined use event +export function undefinedUseEvent< + Event extends keyof AuthClientUseEvents, + Return extends AuthClientUseEvents[Event]["return"] +>( + event: Event, + returnData: Return, + log: LogFunction +): UseEventCallbacks[Event] { + return (() => { + //complain about unset use event callback + log("error", `The use "${event}" event is not defined!`); + return { ...returnData, error: true }; + }) as UseEventCallbacks[Event]; +} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index e69de29..ca14b40 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -0,0 +1,7 @@ +export { AuthClient as default } from "./auth"; +export { + AuthClient, + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, +} from "./auth"; diff --git a/packages/client/src/methods/_template.ts.txt b/packages/client/src/methods/_template.ts.txt new file mode 100644 index 0000000..d3a41a9 --- /dev/null +++ b/packages/client/src/methods/_template.ts.txt @@ -0,0 +1,13 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create _method method +export function create_Method( + internal: AuthInternal +): AuthClientMethod<"_method"> { + return async ({}) => {}; +} diff --git a/packages/client/src/methods/check.ts b/packages/client/src/methods/check.ts new file mode 100644 index 0000000..13af231 --- /dev/null +++ b/packages/client/src/methods/check.ts @@ -0,0 +1,37 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create check method +export function createCheck( + internal: AuthInternal +): AuthClientMethod<"check"> { + return async () => { + const getRefreshToken = await internal.useEventCallbacks.getToken({ + type: "refreshToken", + }); + + if (getRefreshToken.error || getRefreshToken.token === null) + return { clientError: true, res: null }; + + const getAccessToken = await internal.useEventCallbacks.getToken({ + type: "accessToken", + }); + + if (getAccessToken.error || getAccessToken.token === null) + return { clientError: true, res: null }; + + const checkResponse = await ( + internal.config.connector as AuthClientConnector<"check"> + )("check", { + accessToken: getAccessToken.token, + refreshToken: getRefreshToken.token, + }); + + return checkResponse; + }; +} diff --git a/packages/client/src/methods/login.ts b/packages/client/src/methods/login.ts new file mode 100644 index 0000000..c68f2b9 --- /dev/null +++ b/packages/client/src/methods/login.ts @@ -0,0 +1,33 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create login method +export function createLogin( + internal: AuthInternal +): AuthClientMethod<"login"> { + return async ({ login, password }) => { + const loginResponse = await ( + internal.config.connector as AuthClientConnector<"login"> + )("login", { login, password }); + + if (loginResponse.clientError || loginResponse.res.error) + return loginResponse; + + //store tokens + internal.useEventCallbacks.storeToken({ + type: "accessToken", + token: loginResponse.res.data.accessToken, + }); + internal.useEventCallbacks.storeToken({ + type: "refreshToken", + token: loginResponse.res.data.refreshToken, + }); + + return loginResponse; + }; +} diff --git a/packages/client/src/methods/logout.ts b/packages/client/src/methods/logout.ts new file mode 100644 index 0000000..f083364 --- /dev/null +++ b/packages/client/src/methods/logout.ts @@ -0,0 +1,36 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create logout method +export function createLogout( + internal: AuthInternal +): AuthClientMethod<"logout"> { + return async () => { + const getRefreshToken = await internal.useEventCallbacks.getToken({ + type: "refreshToken", + }); + + if (getRefreshToken.error || getRefreshToken.token === null) + return { clientError: true, res: null }; + + const logoutResponse = await ( + internal.config.connector as AuthClientConnector<"logout"> + )("logout", { refreshToken: getRefreshToken.token }); + + if (!logoutResponse.clientError && !logoutResponse.res.error) { + internal.useEventCallbacks.deleteToken({ + type: "accessToken", + }); + internal.useEventCallbacks.deleteToken({ + type: "refreshToken", + }); + } + + return logoutResponse; + }; +} diff --git a/packages/client/src/methods/refresh.ts b/packages/client/src/methods/refresh.ts new file mode 100644 index 0000000..38f9e78 --- /dev/null +++ b/packages/client/src/methods/refresh.ts @@ -0,0 +1,34 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create refresh method +export function createRefresh( + internal: AuthInternal +): AuthClientMethod<"refresh"> { + return async () => { + const getRefreshToken = await internal.useEventCallbacks.getToken({ + type: "refreshToken", + }); + + if (getRefreshToken.error || getRefreshToken.token === null) + return { clientError: true, res: null }; + + const refreshResponse = await ( + internal.config.connector as AuthClientConnector<"refresh"> + )("refresh", { refreshToken: getRefreshToken.token }); + + if (!refreshResponse.clientError && !refreshResponse.res.error) { + internal.useEventCallbacks.storeToken({ + type: "accessToken", + token: refreshResponse.res.data.accessToken, + }); + } + + return refreshResponse; + }; +} diff --git a/packages/client/src/methods/register.ts b/packages/client/src/methods/register.ts new file mode 100644 index 0000000..d8658c7 --- /dev/null +++ b/packages/client/src/methods/register.ts @@ -0,0 +1,20 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create register method +export function createRegister( + internal: AuthInternal +): AuthClientMethod<"register"> { + return async ({ email, username, password }) => { + const registerResponse = await ( + internal.config.connector as AuthClientConnector<"register"> + )("register", { email, username, password }); + + return registerResponse; + }; +} diff --git a/packages/client/src/methods/validate.ts b/packages/client/src/methods/validate.ts new file mode 100644 index 0000000..1160ec0 --- /dev/null +++ b/packages/client/src/methods/validate.ts @@ -0,0 +1,20 @@ +import { AuthInternal } from "@auth-tools/base"; +import { + AuthClientConfig, + AuthClientConnector, + AuthClientMethod, + AuthClientUseEvents, +} from "../auth"; + +//create validate method +export function createValidate( + internal: AuthInternal +): AuthClientMethod<"validate"> { + return async ({ accessToken }) => { + const validateResponse = await ( + internal.config.connector as AuthClientConnector<"validate"> + )("validate", { accessToken }); + + return validateResponse; + }; +}