feat: add logto auth and backend metrics integration
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import LogtoClient from "@logto/chrome-extension";
|
||||
|
||||
import { readAuthConfig } from "../../shared/auth-config";
|
||||
import type { AuthClientLike } from "./types";
|
||||
|
||||
export function createLogtoAuthClient(): AuthClientLike {
|
||||
const config = readAuthConfig();
|
||||
const client = new LogtoClient({
|
||||
appId: config.appId,
|
||||
endpoint: config.logtoEndpoint,
|
||||
resources: [config.apiResource],
|
||||
scopes: config.scopes
|
||||
});
|
||||
|
||||
return {
|
||||
getAccessToken(resource?: string) {
|
||||
return client.getAccessToken(resource);
|
||||
},
|
||||
getIdTokenClaims() {
|
||||
return client.getIdTokenClaims();
|
||||
},
|
||||
isAuthenticated() {
|
||||
return client.isAuthenticated();
|
||||
},
|
||||
signIn() {
|
||||
return client.signIn(readChromeIdentity().getRedirectURL("/callback"));
|
||||
},
|
||||
signOut() {
|
||||
return client.signOut(readChromeIdentity().getRedirectURL());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function readChromeIdentity(): {
|
||||
getRedirectURL: (path?: string) => string;
|
||||
} {
|
||||
const identity = (
|
||||
globalThis as typeof globalThis & {
|
||||
chrome?: {
|
||||
identity?: {
|
||||
getRedirectURL?: (path?: string) => string;
|
||||
};
|
||||
};
|
||||
}
|
||||
).chrome?.identity;
|
||||
|
||||
if (typeof identity?.getRedirectURL !== "function") {
|
||||
throw new Error("chrome.identity.getRedirectURL is unavailable");
|
||||
}
|
||||
|
||||
return {
|
||||
getRedirectURL: identity.getRedirectURL.bind(identity)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { readAuthConfig, type AuthConfig } from "../../shared/auth-config";
|
||||
import { createLoggedInAuthState, createLoggedOutAuthState } from "./state";
|
||||
import type { AuthClientLike } from "./types";
|
||||
|
||||
export interface AuthController {
|
||||
getAccessToken(): Promise<string>;
|
||||
getAuthState(): Promise<ReturnType<typeof createLoggedOutAuthState>>;
|
||||
signIn(): Promise<void>;
|
||||
signOut(): Promise<void>;
|
||||
}
|
||||
|
||||
export function createAuthController(options: {
|
||||
authClient: AuthClientLike;
|
||||
config?: AuthConfig;
|
||||
}): AuthController {
|
||||
const config = options.config ?? readAuthConfig();
|
||||
|
||||
return {
|
||||
async getAccessToken() {
|
||||
return options.authClient.getAccessToken(config.apiResource);
|
||||
},
|
||||
async getAuthState() {
|
||||
const isAuthenticated = await options.authClient.isAuthenticated();
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return createLoggedOutAuthState(config);
|
||||
}
|
||||
|
||||
const claims = await options.authClient.getIdTokenClaims();
|
||||
return createLoggedInAuthState(claims, config);
|
||||
},
|
||||
async signIn() {
|
||||
await options.authClient.signIn();
|
||||
},
|
||||
async signOut() {
|
||||
await options.authClient.signOut();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { AuthConfig } from "../../shared/auth-config";
|
||||
import type { AuthStateValue } from "../../shared/auth-messages";
|
||||
|
||||
export function createLoggedOutAuthState(
|
||||
config?: Pick<AuthConfig, "apiResource">
|
||||
): AuthStateValue {
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
resource: config?.apiResource ?? null
|
||||
};
|
||||
}
|
||||
|
||||
export function createLoggedInAuthState(
|
||||
claims: Record<string, unknown> | null | undefined,
|
||||
config?: Pick<AuthConfig, "apiResource" | "scopes">
|
||||
): AuthStateValue {
|
||||
return {
|
||||
accessTokenExpiresAt: null,
|
||||
isAuthenticated: true,
|
||||
resource: config?.apiResource ?? null,
|
||||
scopes: config?.scopes ?? [],
|
||||
tokenAvailable: true,
|
||||
userInfo: {
|
||||
email: readStringClaim(claims, "email"),
|
||||
name: readStringClaim(claims, "name"),
|
||||
sub: readStringClaim(claims, "sub"),
|
||||
username: readStringClaim(claims, "username")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function readStringClaim(
|
||||
claims: Record<string, unknown> | null | undefined,
|
||||
key: string
|
||||
): string | undefined {
|
||||
const value = claims?.[key];
|
||||
return typeof value === "string" ? value : undefined;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface AuthClientLike {
|
||||
getAccessToken(resource?: string): Promise<string>;
|
||||
getIdTokenClaims(): Promise<Record<string, unknown> | null>;
|
||||
isAuthenticated(): Promise<boolean>;
|
||||
signIn(): Promise<void>;
|
||||
signOut(): Promise<void>;
|
||||
}
|
||||
Reference in New Issue
Block a user