@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.
- package/README.md +185 -50
- package/dist/v2/auth/client/baseClient.d.ts +2 -0
- package/dist/v2/auth/client/baseClient.js +14 -0
- package/dist/v2/auth/client/client.d.ts +3 -0
- package/dist/v2/auth/client/client.js +7 -0
- package/dist/v2/auth/client/index.d.ts +3 -0
- package/dist/v2/auth/client/index.js +3 -0
- package/dist/v2/auth/factory.d.ts +20 -0
- package/dist/v2/auth/factory.js +31 -0
- package/dist/v2/auth/service/baseService.d.ts +10 -0
- package/dist/v2/auth/service/baseService.js +82 -0
- package/dist/v2/auth/service/index.d.ts +3 -0
- package/dist/v2/auth/service/index.js +3 -0
- package/dist/v2/auth/service/service.d.ts +19 -0
- package/dist/v2/auth/service/service.js +162 -0
- package/dist/v2/client/builder/builder.d.ts +7 -0
- package/dist/v2/client/builder/builder.js +14 -0
- package/dist/v2/client/builder/index.d.ts +2 -0
- package/dist/v2/client/builder/index.js +2 -0
- package/dist/v2/client/client.d.ts +7 -0
- package/dist/v2/client/client.js +9 -0
- package/dist/v2/client/index.d.ts +4 -0
- package/dist/v2/client/index.js +4 -0
- package/dist/v2/client/middleware/builder.d.ts +16 -0
- package/dist/v2/client/middleware/builder.js +48 -0
- package/dist/v2/client/middleware/index.d.ts +2 -0
- package/dist/v2/client/middleware/index.js +2 -0
- package/dist/v2/client/middleware/middleware.d.ts +7 -0
- package/dist/v2/client/middleware/middleware.js +39 -0
- package/dist/v2/index.d.ts +11 -0
- package/dist/v2/index.js +7 -0
- package/dist/v2/pkg/errors/errors.d.ts +5 -0
- package/dist/v2/pkg/errors/errors.js +9 -0
- package/dist/v2/pkg/errors/index.d.ts +4 -0
- package/dist/v2/pkg/errors/index.js +2 -0
- package/dist/v2/pkg/localStorage/index.d.ts +2 -0
- package/dist/v2/pkg/localStorage/index.js +2 -0
- package/dist/v2/pkg/localStorage/localStorage.d.ts +20 -0
- package/dist/v2/pkg/localStorage/localStorage.js +34 -0
- package/dist/v2/pkg/pkce/index.d.ts +2 -0
- package/dist/v2/pkg/pkce/index.js +2 -0
- package/dist/v2/pkg/pkce/pkce.d.ts +3 -0
- package/dist/v2/pkg/pkce/pkce.js +30 -0
- package/dist/v2/pkg/types/authClient.d.ts +3 -0
- package/dist/{types.js → v2/pkg/types/authClient.js} +0 -1
- package/dist/v2/pkg/types/authService.d.ts +42 -0
- package/dist/v2/pkg/types/authService.js +1 -0
- package/dist/v2/pkg/types/index.d.ts +3 -0
- package/dist/v2/pkg/types/index.js +1 -0
- package/dist/v2/pkg/url/index.d.ts +2 -0
- package/dist/v2/pkg/url/index.js +2 -0
- package/dist/v2/pkg/url/url.d.ts +8 -0
- package/dist/v2/pkg/url/url.js +40 -0
- package/dist/v2/vue/router-guard/guard.d.ts +27 -0
- package/dist/v2/vue/router-guard/guard.js +105 -0
- package/dist/v2/vue/router-guard/index.d.ts +1 -0
- package/dist/v2/vue/router-guard/index.js +1 -0
- package/package.json +16 -9
- package/dist/client/client.d.ts +0 -2
- package/dist/client/client.js +0 -14
- package/dist/client/middleware.d.ts +0 -15
- package/dist/client/middleware.js +0 -51
- package/dist/client/storage/localStorage.d.ts +0 -8
- package/dist/client/storage/localStorage.js +0 -15
- package/dist/guard/index.d.ts +0 -11
- package/dist/guard/index.js +0 -62
- package/dist/index.d.ts +0 -26
- package/dist/index.js +0 -160
- package/dist/types.d.ts +0 -16
|
@@ -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,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 };
|
package/dist/v2/index.js
ADDED
|
@@ -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,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,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,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,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": "
|
|
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
|
-
"
|
|
11
|
+
"types": "./dist/v2/index.d.ts",
|
|
12
|
+
"import": "./dist/v2/index.js"
|
|
12
13
|
},
|
|
13
|
-
"./
|
|
14
|
-
"
|
|
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
|
-
"
|
|
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": {
|
package/dist/client/client.d.ts
DELETED
package/dist/client/client.js
DELETED
|
@@ -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
|
-
};
|