@dehwyyy/auth 1.0.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +185 -50
  2. package/dist/v2/auth/client/baseClient.d.ts +2 -0
  3. package/dist/v2/auth/client/baseClient.js +14 -0
  4. package/dist/v2/auth/client/client.d.ts +3 -0
  5. package/dist/v2/auth/client/client.js +7 -0
  6. package/dist/v2/auth/client/index.d.ts +3 -0
  7. package/dist/v2/auth/client/index.js +3 -0
  8. package/dist/v2/auth/factory.d.ts +20 -0
  9. package/dist/v2/auth/factory.js +31 -0
  10. package/dist/v2/auth/service/baseService.d.ts +10 -0
  11. package/dist/v2/auth/service/baseService.js +82 -0
  12. package/dist/v2/auth/service/index.d.ts +3 -0
  13. package/dist/v2/auth/service/index.js +3 -0
  14. package/dist/v2/auth/service/service.d.ts +19 -0
  15. package/dist/v2/auth/service/service.js +162 -0
  16. package/dist/v2/client/builder/builder.d.ts +7 -0
  17. package/dist/v2/client/builder/builder.js +14 -0
  18. package/dist/v2/client/builder/index.d.ts +2 -0
  19. package/dist/v2/client/builder/index.js +2 -0
  20. package/dist/v2/client/client.d.ts +7 -0
  21. package/dist/v2/client/client.js +9 -0
  22. package/dist/v2/client/index.d.ts +4 -0
  23. package/dist/v2/client/index.js +4 -0
  24. package/dist/v2/client/middleware/builder.d.ts +16 -0
  25. package/dist/v2/client/middleware/builder.js +48 -0
  26. package/dist/v2/client/middleware/index.d.ts +2 -0
  27. package/dist/v2/client/middleware/index.js +2 -0
  28. package/dist/v2/client/middleware/middleware.d.ts +7 -0
  29. package/dist/v2/client/middleware/middleware.js +39 -0
  30. package/dist/v2/index.d.ts +11 -0
  31. package/dist/v2/index.js +7 -0
  32. package/dist/v2/pkg/errors/errors.d.ts +5 -0
  33. package/dist/v2/pkg/errors/errors.js +9 -0
  34. package/dist/v2/pkg/errors/index.d.ts +4 -0
  35. package/dist/v2/pkg/errors/index.js +2 -0
  36. package/dist/v2/pkg/localStorage/index.d.ts +2 -0
  37. package/dist/v2/pkg/localStorage/index.js +2 -0
  38. package/dist/v2/pkg/localStorage/localStorage.d.ts +20 -0
  39. package/dist/v2/pkg/localStorage/localStorage.js +34 -0
  40. package/dist/v2/pkg/pkce/index.d.ts +2 -0
  41. package/dist/v2/pkg/pkce/index.js +2 -0
  42. package/dist/v2/pkg/pkce/pkce.d.ts +3 -0
  43. package/dist/v2/pkg/pkce/pkce.js +30 -0
  44. package/dist/v2/pkg/types/authClient.d.ts +3 -0
  45. package/dist/{types.js → v2/pkg/types/authClient.js} +0 -1
  46. package/dist/v2/pkg/types/authService.d.ts +42 -0
  47. package/dist/v2/pkg/types/authService.js +1 -0
  48. package/dist/v2/pkg/types/index.d.ts +3 -0
  49. package/dist/v2/pkg/types/index.js +1 -0
  50. package/dist/v2/pkg/url/index.d.ts +2 -0
  51. package/dist/v2/pkg/url/index.js +2 -0
  52. package/dist/v2/pkg/url/url.d.ts +8 -0
  53. package/dist/v2/pkg/url/url.js +40 -0
  54. package/dist/v2/vue/router-guard/guard.d.ts +27 -0
  55. package/dist/v2/vue/router-guard/guard.js +105 -0
  56. package/dist/v2/vue/router-guard/index.d.ts +1 -0
  57. package/dist/v2/vue/router-guard/index.js +1 -0
  58. package/package.json +16 -9
  59. package/dist/client/client.d.ts +0 -2
  60. package/dist/client/client.js +0 -14
  61. package/dist/client/middleware.d.ts +0 -15
  62. package/dist/client/middleware.js +0 -51
  63. package/dist/client/storage/localStorage.d.ts +0 -8
  64. package/dist/client/storage/localStorage.js +0 -15
  65. package/dist/guard/index.d.ts +0 -11
  66. package/dist/guard/index.js +0 -62
  67. package/dist/index.d.ts +0 -26
  68. package/dist/index.js +0 -160
  69. package/dist/types.d.ts +0 -16
@@ -0,0 +1,9 @@
1
+ export class ClientInstance {
2
+ static map = new Map();
3
+ New(key, clientFactory) {
4
+ if (!ClientInstance.map.has(key)) {
5
+ ClientInstance.map.set(key, clientFactory());
6
+ }
7
+ return ClientInstance.map.get(key);
8
+ }
9
+ }
@@ -0,0 +1,4 @@
1
+ import { ClientBuilder } from "../../v2/client/builder/index.js";
2
+ import { ClientInstance } from "../../v2/client/client.js";
3
+ import { MiddlewareBuilder } from "../../v2/client/middleware/index.js";
4
+ export { ClientBuilder, ClientInstance, MiddlewareBuilder };
@@ -0,0 +1,4 @@
1
+ import { ClientBuilder } from "../../v2/client/builder/index.js";
2
+ import { ClientInstance } from "../../v2/client/client.js";
3
+ import { MiddlewareBuilder } from "../../v2/client/middleware/index.js";
4
+ export { ClientBuilder, ClientInstance, MiddlewareBuilder };
@@ -0,0 +1,16 @@
1
+ import { BaseAuthService } from '../../../v2/pkg/types/index.js';
2
+ import type { Middleware as OpenAPIMiddleware } from 'openapi-fetch';
3
+ export declare class MiddlewareBuilder {
4
+ private service;
5
+ private fetchImpl;
6
+ private hasRequestClone;
7
+ private requestCloneMw;
8
+ private middlewares;
9
+ constructor(service: BaseAuthService, fetchImpl?: typeof fetch);
10
+ private addRequestClone;
11
+ withRequestClone(): this;
12
+ withAccessToken(): this;
13
+ withAutoRefresh(): this;
14
+ buildDefault(): OpenAPIMiddleware[];
15
+ build(): OpenAPIMiddleware[];
16
+ }
@@ -0,0 +1,48 @@
1
+ import { Middleware } from '../../../v2/client/middleware/middleware.js';
2
+ export class MiddlewareBuilder {
3
+ service;
4
+ fetchImpl;
5
+ hasRequestClone = false;
6
+ requestCloneMw = null;
7
+ middlewares = [];
8
+ constructor(service, fetchImpl = fetch) {
9
+ this.service = service;
10
+ this.fetchImpl = fetchImpl;
11
+ }
12
+ addRequestClone() {
13
+ if (!this.hasRequestClone) {
14
+ this.requestCloneMw = Middleware.requestClone();
15
+ this.hasRequestClone = true;
16
+ }
17
+ }
18
+ withRequestClone() {
19
+ this.addRequestClone();
20
+ return this;
21
+ }
22
+ withAccessToken() {
23
+ this.middlewares.push(Middleware.accessTokenAttacher(this.service));
24
+ return this;
25
+ }
26
+ // autoRefresh retries the original request after a successful refresh, so it
27
+ // implicitly requires the request clone middleware. Enabling it pulls in the
28
+ // clone automatically.
29
+ withAutoRefresh() {
30
+ this.addRequestClone();
31
+ this.middlewares.push(Middleware.autoRefresh(this.service, this.fetchImpl));
32
+ return this;
33
+ }
34
+ buildDefault() {
35
+ return [
36
+ Middleware.requestClone(),
37
+ Middleware.accessTokenAttacher(this.service),
38
+ Middleware.autoRefresh(this.service, this.fetchImpl),
39
+ ];
40
+ }
41
+ build() {
42
+ const result = [];
43
+ if (this.requestCloneMw) {
44
+ result.push(this.requestCloneMw);
45
+ }
46
+ return result.concat(this.middlewares);
47
+ }
48
+ }
@@ -0,0 +1,2 @@
1
+ import { MiddlewareBuilder } from "../../../v2/client/middleware/builder.js";
2
+ export { MiddlewareBuilder };
@@ -0,0 +1,2 @@
1
+ import { MiddlewareBuilder } from "../../../v2/client/middleware/builder.js";
2
+ export { MiddlewareBuilder };
@@ -0,0 +1,7 @@
1
+ import { BaseAuthService } from '../../../v2/pkg/types/index.js';
2
+ import type { Middleware as OpenAPIMiddleware } from 'openapi-fetch';
3
+ export declare class Middleware {
4
+ static requestClone(): OpenAPIMiddleware;
5
+ static accessTokenAttacher(service: BaseAuthService): OpenAPIMiddleware;
6
+ static autoRefresh(service: BaseAuthService, fetchImpl?: typeof fetch): OpenAPIMiddleware;
7
+ }
@@ -0,0 +1,39 @@
1
+ export class Middleware {
2
+ static requestClone() {
3
+ return {
4
+ async onRequest({ request }) {
5
+ const methodsWithBody = ['POST', 'PUT', 'PATCH', 'DELETE'];
6
+ if (methodsWithBody.includes(request.method.toUpperCase())) {
7
+ request._clone = request.clone();
8
+ }
9
+ return request;
10
+ },
11
+ };
12
+ }
13
+ static accessTokenAttacher(service) {
14
+ return {
15
+ async onRequest({ request }) {
16
+ return service.RequestWithAccessToken(request);
17
+ }
18
+ };
19
+ }
20
+ static autoRefresh(service, fetchImpl = fetch) {
21
+ return {
22
+ async onResponse({ response, request }) {
23
+ if (response.status !== 401 || request.url.includes('/oauth/v2/token')) {
24
+ return response;
25
+ }
26
+ const requestToRetry = request._clone || request;
27
+ const newAccessToken = await service.Refresh()
28
+ .catch((e) => {
29
+ console.error('failed to refresh', e);
30
+ return null;
31
+ });
32
+ if (!newAccessToken) {
33
+ return response;
34
+ }
35
+ return fetchImpl(service.RequestWithAccessToken(requestToRetry, newAccessToken));
36
+ },
37
+ };
38
+ }
39
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseAuthClientInstance, CreateAuthClientInstance } from "../v2/auth/client/index.js";
2
+ import { createAuth } from "../v2/auth/factory.js";
3
+ import type { Auth } from "../v2/auth/factory.js";
4
+ import { AuthService, BaseAuthService } from "../v2/auth/service/index.js";
5
+ import { ClientBuilder, ClientInstance, MiddlewareBuilder } from "../v2/client/index.js";
6
+ import { CallbackError } from "../v2/pkg/errors/index.js";
7
+ import type { CallbackErrorCode } from "../v2/pkg/errors/index.js";
8
+ import type { GetMeResponse, HandleCallbackResult, RouteLocation, VerboseUserInfo, ZitadelConfig } from "../v2/pkg/types/index.js";
9
+ import { RouterAuthGuard } from "../v2/vue/router-guard/index.js";
10
+ export { AuthService, BaseAuthClientInstance, BaseAuthService, CallbackError, ClientBuilder, ClientInstance, createAuth, CreateAuthClientInstance, MiddlewareBuilder, RouterAuthGuard };
11
+ export type { Auth, CallbackErrorCode, GetMeResponse, HandleCallbackResult, RouteLocation, VerboseUserInfo, ZitadelConfig };
@@ -0,0 +1,7 @@
1
+ import { BaseAuthClientInstance, CreateAuthClientInstance } from "../v2/auth/client/index.js";
2
+ import { createAuth } from "../v2/auth/factory.js";
3
+ import { AuthService, BaseAuthService } from "../v2/auth/service/index.js";
4
+ import { ClientBuilder, ClientInstance, MiddlewareBuilder } from "../v2/client/index.js";
5
+ import { CallbackError } from "../v2/pkg/errors/index.js";
6
+ import { RouterAuthGuard } from "../v2/vue/router-guard/index.js";
7
+ export { AuthService, BaseAuthClientInstance, BaseAuthService, CallbackError, ClientBuilder, ClientInstance, createAuth, CreateAuthClientInstance, MiddlewareBuilder, RouterAuthGuard };
@@ -0,0 +1,5 @@
1
+ export type CallbackErrorCode = "state_mismatch" | "verifier_missing" | "code_missing" | "exchange_failed";
2
+ export declare class CallbackError extends Error {
3
+ readonly code: CallbackErrorCode;
4
+ constructor(code: CallbackErrorCode, message?: string);
5
+ }
@@ -0,0 +1,9 @@
1
+ export class CallbackError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message ?? code);
5
+ this.name = "CallbackError";
6
+ this.code = code;
7
+ Object.setPrototypeOf(this, CallbackError.prototype);
8
+ }
9
+ }
@@ -0,0 +1,4 @@
1
+ import { CallbackError } from "../../../v2/pkg/errors/errors.js";
2
+ import type { CallbackErrorCode } from "../../../v2/pkg/errors/errors.js";
3
+ export { CallbackError };
4
+ export type { CallbackErrorCode };
@@ -0,0 +1,2 @@
1
+ import { CallbackError } from "../../../v2/pkg/errors/errors.js";
2
+ export { CallbackError };
@@ -0,0 +1,2 @@
1
+ import { SessionKey, SessionStore, Storage, StorageKey } from "../../../v2/pkg/localStorage/localStorage.js";
2
+ export { SessionKey, SessionStore, Storage, StorageKey };
@@ -0,0 +1,2 @@
1
+ import { SessionKey, SessionStore, Storage, StorageKey } from "../../../v2/pkg/localStorage/localStorage.js";
2
+ export { SessionKey, SessionStore, Storage, StorageKey };
@@ -0,0 +1,20 @@
1
+ export declare const enum StorageKey {
2
+ ACCESS_TOKEN = "accessToken",
3
+ REFRESH_TOKEN = "refreshToken",
4
+ ID_TOKEN = "idToken"
5
+ }
6
+ export declare class Storage {
7
+ static Set(key: StorageKey, value: string): void;
8
+ static Delete(key: StorageKey): void;
9
+ static Get(key: StorageKey): string | null;
10
+ }
11
+ export declare const enum SessionKey {
12
+ PKCE_VERIFIER = "pkce_verifier",
13
+ PKCE_STATE = "pkce_state",
14
+ RETURN_TO = "pkce_return_to"
15
+ }
16
+ export declare class SessionStore {
17
+ static Set(key: SessionKey, value: string): void;
18
+ static Delete(key: SessionKey): void;
19
+ static Get(key: SessionKey): string | null;
20
+ }
@@ -0,0 +1,34 @@
1
+ export var StorageKey;
2
+ (function (StorageKey) {
3
+ StorageKey["ACCESS_TOKEN"] = "accessToken";
4
+ StorageKey["REFRESH_TOKEN"] = "refreshToken";
5
+ StorageKey["ID_TOKEN"] = "idToken";
6
+ })(StorageKey || (StorageKey = {}));
7
+ export class Storage {
8
+ static Set(key, value) {
9
+ localStorage.setItem(key, value);
10
+ }
11
+ static Delete(key) {
12
+ localStorage.removeItem(key);
13
+ }
14
+ static Get(key) {
15
+ return localStorage.getItem(key);
16
+ }
17
+ }
18
+ export var SessionKey;
19
+ (function (SessionKey) {
20
+ SessionKey["PKCE_VERIFIER"] = "pkce_verifier";
21
+ SessionKey["PKCE_STATE"] = "pkce_state";
22
+ SessionKey["RETURN_TO"] = "pkce_return_to";
23
+ })(SessionKey || (SessionKey = {}));
24
+ export class SessionStore {
25
+ static Set(key, value) {
26
+ sessionStorage.setItem(key, value);
27
+ }
28
+ static Delete(key) {
29
+ sessionStorage.removeItem(key);
30
+ }
31
+ static Get(key) {
32
+ return sessionStorage.getItem(key);
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ import { challengeS256, generateState, generateVerifier } from "../../../v2/pkg/pkce/pkce.js";
2
+ export { challengeS256, generateState, generateVerifier };
@@ -0,0 +1,2 @@
1
+ import { challengeS256, generateState, generateVerifier } from "../../../v2/pkg/pkce/pkce.js";
2
+ export { challengeS256, generateState, generateVerifier };
@@ -0,0 +1,3 @@
1
+ export declare function generateVerifier(length?: number): string;
2
+ export declare function generateState(): string;
3
+ export declare function challengeS256(verifier: string): Promise<string>;
@@ -0,0 +1,30 @@
1
+ const VERIFIER_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
2
+ function randomBytes(length) {
3
+ const bytes = new Uint8Array(length);
4
+ crypto.getRandomValues(bytes);
5
+ return bytes;
6
+ }
7
+ function base64UrlEncode(bytes) {
8
+ let binary = "";
9
+ for (let i = 0; i < bytes.length; i++) {
10
+ binary += String.fromCharCode(bytes[i]);
11
+ }
12
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
13
+ }
14
+ export function generateVerifier(length = 64) {
15
+ const size = Math.min(128, Math.max(43, length));
16
+ const bytes = randomBytes(size);
17
+ let verifier = "";
18
+ for (let i = 0; i < size; i++) {
19
+ verifier += VERIFIER_CHARSET[bytes[i] % VERIFIER_CHARSET.length];
20
+ }
21
+ return verifier;
22
+ }
23
+ export function generateState() {
24
+ return base64UrlEncode(randomBytes(32));
25
+ }
26
+ export async function challengeS256(verifier) {
27
+ const data = new TextEncoder().encode(verifier);
28
+ const digest = await crypto.subtle.digest("SHA-256", data);
29
+ return base64UrlEncode(new Uint8Array(digest));
30
+ }
@@ -0,0 +1,3 @@
1
+ import { paths } from "@dehwyyy/auth/v2/pkg/types/schema";
2
+ import createClient from "openapi-fetch";
3
+ export type HttpAuthClient = ReturnType<typeof createClient<paths>>;
@@ -1,2 +1 @@
1
- ;
2
1
  export {};
@@ -0,0 +1,42 @@
1
+ import { components } from "@dehwyyy/auth/v2/pkg/types/schema";
2
+ type UserInfo = components["schemas"]["UserInfo"];
3
+ export type ZitadelConfig = {
4
+ issuer: string;
5
+ clientId: string;
6
+ redirectUri: string;
7
+ postLogoutRedirectUri?: string;
8
+ scopes?: string[];
9
+ };
10
+ export type VerboseUserInfo = {
11
+ username?: UserInfo["preferred_username"];
12
+ email?: UserInfo["email"];
13
+ email_verified?: UserInfo["email_verified"];
14
+ name?: UserInfo["name"];
15
+ picture?: UserInfo["picture"];
16
+ };
17
+ export type GetMeResponse = {
18
+ user_id: UserInfo["sub"];
19
+ roles: string[];
20
+ active: boolean;
21
+ info?: VerboseUserInfo;
22
+ };
23
+ export type HandleCallbackResult = {
24
+ me: GetMeResponse | null;
25
+ returnTo: string | null;
26
+ };
27
+ export type RouteLocation = {
28
+ path: string;
29
+ fullPath: string;
30
+ query: Record<string, string | null | (string | null)[] | undefined>;
31
+ };
32
+ export interface BaseAuthService {
33
+ RequestWithAccessToken(req: Request, token?: string | null): Request;
34
+ Refresh(): Promise<string | null>;
35
+ }
36
+ export interface AuthService {
37
+ Login(returnTo?: string): Promise<void>;
38
+ HandleCallback(): Promise<HandleCallbackResult>;
39
+ GetMe(verbose?: boolean): Promise<GetMeResponse | null>;
40
+ Logout(): Promise<void>;
41
+ }
42
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { HttpAuthClient } from "../../../v2/pkg/types/authClient.js";
2
+ import type { AuthService, BaseAuthService, GetMeResponse, HandleCallbackResult, RouteLocation, VerboseUserInfo, ZitadelConfig } from "../../../v2/pkg/types/authService.js";
3
+ export { AuthService, BaseAuthService, GetMeResponse, HandleCallbackResult, HttpAuthClient, RouteLocation, VerboseUserInfo, ZitadelConfig };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { normalizeIssuer, pathnameOf, sanitizeReturnTo } from "../../../v2/pkg/url/url.js";
2
+ export { normalizeIssuer, pathnameOf, sanitizeReturnTo };
@@ -0,0 +1,2 @@
1
+ import { normalizeIssuer, pathnameOf, sanitizeReturnTo } from "../../../v2/pkg/url/url.js";
2
+ export { normalizeIssuer, pathnameOf, sanitizeReturnTo };
@@ -0,0 +1,8 @@
1
+ export declare function normalizeIssuer(issuer: string): string;
2
+ export declare function pathnameOf(url: string): string;
3
+ /**
4
+ * Returns `returnTo` only when it is safe to redirect to (relative path or
5
+ * same-origin absolute URL). Absolute URLs pointing at a foreign origin are
6
+ * rejected (returns null) to close open-redirect vectors inside the library.
7
+ */
8
+ export declare function sanitizeReturnTo(returnTo: string | null): string | null;
@@ -0,0 +1,40 @@
1
+ export function normalizeIssuer(issuer) {
2
+ return issuer.replace(/\/+$/, "");
3
+ }
4
+ export function pathnameOf(url) {
5
+ try {
6
+ return new URL(url, "http://localhost").pathname;
7
+ }
8
+ catch {
9
+ return url;
10
+ }
11
+ }
12
+ /**
13
+ * Returns `returnTo` only when it is safe to redirect to (relative path or
14
+ * same-origin absolute URL). Absolute URLs pointing at a foreign origin are
15
+ * rejected (returns null) to close open-redirect vectors inside the library.
16
+ */
17
+ export function sanitizeReturnTo(returnTo) {
18
+ if (!returnTo) {
19
+ return null;
20
+ }
21
+ // Single-slash absolute path only. Reject protocol-relative forms: a second
22
+ // "/" or a "\" after the first slash (browsers normalize "\" -> "/", so
23
+ // "/\evil.com/x" would otherwise navigate cross-origin).
24
+ if (/^\/(?![/\\])/.test(returnTo)) {
25
+ return returnTo;
26
+ }
27
+ if (typeof location === "undefined") {
28
+ return null;
29
+ }
30
+ try {
31
+ const parsed = new URL(returnTo, location.origin);
32
+ if (parsed.origin === location.origin) {
33
+ return parsed.pathname + parsed.search + parsed.hash;
34
+ }
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ return null;
40
+ }
@@ -0,0 +1,27 @@
1
+ import { AuthService, GetMeResponse, RouteLocation } from "../../../v2/pkg/types/index.js";
2
+ type User = Omit<GetMeResponse, "info">;
3
+ interface RouterAuthGuardProps {
4
+ callbackPath: string;
5
+ cacheTTL?: number;
6
+ onGetMe: (data: User | null) => void;
7
+ fallbackPage: string;
8
+ baseServiceURL: string;
9
+ }
10
+ type DynamicUnauthorizedRedirect = {
11
+ to: string;
12
+ roles: string[];
13
+ }[];
14
+ export declare class RouterAuthGuard {
15
+ private authService;
16
+ private props;
17
+ private getMeCache;
18
+ constructor(authService: AuthService, props: RouterAuthGuardProps);
19
+ private cacheTTL;
20
+ private queryValue;
21
+ private hasCode;
22
+ private isCallback;
23
+ Authorized(roles?: string[]): (to: RouteLocation, _from: RouteLocation) => Promise<string | boolean>;
24
+ Unauthorized(redirectProps: DynamicUnauthorizedRedirect): (_to: RouteLocation, _from: RouteLocation) => Promise<string | true>;
25
+ private RolesIntercept;
26
+ }
27
+ export {};
@@ -0,0 +1,105 @@
1
+ const DEFAULT_CACHE_TTL_MS = 15_000;
2
+ export class RouterAuthGuard {
3
+ authService;
4
+ props;
5
+ getMeCache = null;
6
+ constructor(authService, props) {
7
+ this.authService = authService;
8
+ this.props = props;
9
+ }
10
+ cacheTTL() {
11
+ return this.props.cacheTTL ?? DEFAULT_CACHE_TTL_MS;
12
+ }
13
+ queryValue(value) {
14
+ const v = Array.isArray(value) ? value[0] : value;
15
+ return typeof v === "string" ? v : "";
16
+ }
17
+ hasCode(query) {
18
+ return Boolean(this.queryValue(query.code)) && Boolean(this.queryValue(query.state));
19
+ }
20
+ // The callback may only be entered on the configured callbackPath with both
21
+ // code and state present. Matching any route with ?code&state would let an
22
+ // attacker bypass the guard by appending those params to any protected URL.
23
+ isCallback(to) {
24
+ if (to.path !== this.props.callbackPath) {
25
+ return false;
26
+ }
27
+ if (this.hasCode(to.query ?? {})) {
28
+ return true;
29
+ }
30
+ if (typeof location !== "undefined" && location.search) {
31
+ const params = new URLSearchParams(location.search);
32
+ return Boolean(params.get("code")) && Boolean(params.get("state"));
33
+ }
34
+ return false;
35
+ }
36
+ Authorized(roles = []) {
37
+ return async (to, _from) => {
38
+ // Let the callback page resolve the OIDC redirect; do not bounce to login.
39
+ if (this.isCallback(to)) {
40
+ return true;
41
+ }
42
+ // If cache is valid
43
+ if (this.getMeCache && this.getMeCache.expiresAt > Date.now()) {
44
+ return this.RolesIntercept(roles, this.getMeCache.data.roles || [])
45
+ ? true
46
+ : this.props.fallbackPage;
47
+ }
48
+ try {
49
+ const data = await this.authService.GetMe();
50
+ this.props.onGetMe(data);
51
+ if (!data) {
52
+ this.getMeCache = null;
53
+ await this.authService.Login(this.props.baseServiceURL + to.fullPath);
54
+ return false;
55
+ }
56
+ this.getMeCache = {
57
+ data,
58
+ expiresAt: Date.now() + this.cacheTTL(),
59
+ };
60
+ if (!this.RolesIntercept(roles, data.roles || [])) {
61
+ return this.props.fallbackPage;
62
+ }
63
+ return true;
64
+ }
65
+ catch (e) {
66
+ console.error("failed to authorize:", e);
67
+ }
68
+ return false;
69
+ };
70
+ }
71
+ Unauthorized(redirectProps) {
72
+ return async (_to, _from) => {
73
+ // If cache is valid
74
+ let roles = [];
75
+ // Try to get user from cache
76
+ if (this.getMeCache && this.getMeCache.expiresAt > Date.now()) {
77
+ roles = this.getMeCache.data.roles || [];
78
+ }
79
+ else {
80
+ // Or get user from API
81
+ const user = await this.authService.GetMe();
82
+ this.props.onGetMe(user);
83
+ if (user) {
84
+ this.getMeCache = {
85
+ data: user,
86
+ expiresAt: Date.now() + this.cacheTTL(),
87
+ };
88
+ roles = user.roles || [];
89
+ }
90
+ }
91
+ for (const { to, roles: requiredRoles } of redirectProps) {
92
+ if (this.RolesIntercept(requiredRoles, roles)) {
93
+ return to;
94
+ }
95
+ }
96
+ return true;
97
+ };
98
+ }
99
+ RolesIntercept(requiredRoles, userRoles) {
100
+ if (requiredRoles.length === 0) {
101
+ return true;
102
+ }
103
+ return requiredRoles.filter(role => userRoles.includes(role)).length !== 0;
104
+ }
105
+ }
@@ -0,0 +1 @@
1
+ export { RouterAuthGuard } from "../../../v2/vue/router-guard/guard.js";
@@ -0,0 +1 @@
1
+ export { RouterAuthGuard } from "../../../v2/vue/router-guard/guard.js";
package/package.json CHANGED
@@ -1,27 +1,31 @@
1
1
  {
2
2
  "name": "@dehwyyy/auth",
3
- "version": "1.0.5",
3
+ "version": "2.0.0",
4
4
  "private": false,
5
5
  "description": "dehwyyy auth utilities",
6
6
  "type": "module",
7
- "module": "dist/index.js",
8
- "types": "dist/index.d.ts",
7
+ "module": "dist/v2/index.js",
8
+ "types": "dist/v2/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "import": "./dist/index.js"
11
+ "types": "./dist/v2/index.d.ts",
12
+ "import": "./dist/v2/index.js"
12
13
  },
13
- "./guard": {
14
- "import": "./dist/guard/index.js"
14
+ "./v2": {
15
+ "types": "./dist/v2/index.d.ts",
16
+ "import": "./dist/v2/index.js"
15
17
  }
16
18
  },
17
19
  "files": [
18
20
  "dist"
19
21
  ],
20
22
  "scripts": {
21
- "build": "tsc",
23
+ "gen": "openapi-typescript ./spec/zitadel-oidc.yaml -o ./src/v2/pkg/types/schema.d.ts",
24
+ "build": "tsc && tsc-alias --resolve-full-paths",
22
25
  "prepack": "npm run build",
23
26
  "lint": "prettier --check .",
24
- "ts": "tsc",
27
+ "ts": "tsc && tsc-alias --resolve-full-paths",
28
+ "test": "bun test",
25
29
  "format": "prettier --write ."
26
30
  },
27
31
  "engines": {
@@ -32,10 +36,13 @@
32
36
  "dehwyyy",
33
37
  "dehwyyyauth"
34
38
  ],
35
- "author": "",
39
+ "author": "dehwyy",
36
40
  "license": "MIT",
37
41
  "devDependencies": {
42
+ "@types/bun": "^1.1.0",
43
+ "openapi-typescript": "^7.4.0",
38
44
  "prettier": "^3.0.0",
45
+ "tsc-alias": "^1.8.16",
39
46
  "typescript": "5.8.2"
40
47
  },
41
48
  "dependencies": {
@@ -1,2 +0,0 @@
1
- import type { paths } from './schema';
2
- export declare const getClient: (app: string, baseUrl: string, redirectBaseUrl: string, redirectUriPrefix?: string) => import("openapi-fetch").Client<paths, `${string}/${string}`>;
@@ -1,14 +0,0 @@
1
- import createClient from 'openapi-fetch';
2
- import { Middleware } from './middleware';
3
- let client = null;
4
- export const getClient = (app, baseUrl, redirectBaseUrl, redirectUriPrefix = "") => {
5
- if (!client) {
6
- client = createClient({
7
- baseUrl,
8
- credentials: 'include',
9
- });
10
- const middleware = new Middleware(app, baseUrl, redirectBaseUrl, redirectUriPrefix);
11
- client.use(middleware.RequestCloner, middleware.AuthorizationHeaderAttacher, middleware.TokenRefresher);
12
- }
13
- return client;
14
- };