shl: gemeinsamer Bearer-Helfer fuer Frontends #417
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
// UX-09: Backend-for-Frontend-Route, die die externe Workflow-
|
||||
// Rueckkanal-API (VOR-04, GET /objects/{module}/{type}/{id}/context)
|
||||
@@ -23,8 +24,7 @@ export async function GET(request: NextRequest) {
|
||||
const objectId = request.nextUrl.searchParams.get("id");
|
||||
const userId = request.nextUrl.searchParams.get("user");
|
||||
const tenantSlug = request.nextUrl.searchParams.get("tenant");
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
const bearer = auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
const bearer = bearerHeader(request);
|
||||
if (!module || !objectType || !objectId || (!bearer && (!userId || !tenantSlug))) {
|
||||
return NextResponse.json({ error: "'module', 'type', 'id' sowie Bearer-Token oder 'user' und 'tenant' sind erforderlich" }, { status: 400 });
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// UX-03: Zugriff auf die Mail-API (mailapi, Bearer/Core-Modul-Token) mit
|
||||
// demselben Muster wie DOC-05/UX-10 im pdf-viewer (web/pdf-viewer/lib/http.ts)
|
||||
// -- zentral ueber @nexarch/shl (SHL-01), nicht neu erfunden.
|
||||
import { createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
import { createBearerClient, createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
|
||||
export function authMode(): "token" | "caller" {
|
||||
return process.env.NEXT_PUBLIC_AUTH_MODE === "caller" ? "caller" : "token";
|
||||
@@ -24,30 +24,17 @@ export function getSession(): Session {
|
||||
return session;
|
||||
}
|
||||
|
||||
let workflowSession: Session | undefined;
|
||||
|
||||
export function workflowBearerEnabled(): boolean {
|
||||
return authMode() === "token" && Boolean(process.env.NEXT_PUBLIC_CORE_API_BASE);
|
||||
}
|
||||
|
||||
export function getWorkflowSession(): Session {
|
||||
if (!workflowSession) {
|
||||
workflowSession = createSession({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE as string,
|
||||
module: "workflow",
|
||||
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
|
||||
});
|
||||
}
|
||||
return workflowSession;
|
||||
}
|
||||
const workflow = createBearerClient("workflow", () => ({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE,
|
||||
loginUrl: process.env.NEXT_PUBLIC_LOGIN_URL,
|
||||
}));
|
||||
|
||||
export function workflowFetch(input: string, init?: RequestInit): Promise<Response> {
|
||||
return workflowBearerEnabled() ? getWorkflowSession().fetch(input, init) : fetch(input, init);
|
||||
return authMode() === "token" ? workflow.apiFetch(input, init) : fetch(input, init);
|
||||
}
|
||||
|
||||
export function resetSessionForTests(): void {
|
||||
session = undefined;
|
||||
workflowSession = undefined;
|
||||
}
|
||||
|
||||
export function apiFetch(input: string, init?: RequestInit): Promise<Response> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.ROUTING_RULES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.ROUTING_RULES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.ROUTING_RULES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.ROUTING_RULES_API_URL;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export function bearerHeader(request: NextRequest): string | null {
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
return auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
}
|
||||
@@ -1,29 +1,6 @@
|
||||
import { createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
import { createBearerClient } from "@nexarch/shl";
|
||||
|
||||
let session: Session | undefined;
|
||||
|
||||
export function bearerEnabled(): boolean {
|
||||
return Boolean(process.env.NEXT_PUBLIC_CORE_API_BASE);
|
||||
}
|
||||
|
||||
export function getSession(): Session {
|
||||
if (!session) {
|
||||
session = createSession({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE as string,
|
||||
module: "workflow",
|
||||
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
|
||||
});
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
export function apiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||
return bearerEnabled() ? getSession().fetch(input, init) : fetch(input, init);
|
||||
}
|
||||
|
||||
export async function sessionUserId(): Promise<string> {
|
||||
if (!bearerEnabled()) return "";
|
||||
const s = getSession();
|
||||
await s.token();
|
||||
return s.identity()?.userId ?? "";
|
||||
}
|
||||
export const { bearerEnabled, getSession, apiFetch, sessionUserId } = createBearerClient("workflow", () => ({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE,
|
||||
loginUrl: process.env.NEXT_PUBLIC_LOGIN_URL,
|
||||
}));
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { bearerHeader, createBearerClient } from "../auth/bearer";
|
||||
|
||||
const req = (auth?: string) => ({ headers: new Headers(auth ? { authorization: auth } : {}) });
|
||||
|
||||
describe("bearerHeader", () => {
|
||||
it("liefert den Header bei Bearer mit Token", () => {
|
||||
expect(bearerHeader(req("Bearer abc"))).toBe("Bearer abc");
|
||||
});
|
||||
it.each([undefined, "Bearer ", "Basic abc"])("liefert null bei %s", (h) => {
|
||||
expect(bearerHeader(req(h))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createBearerClient", () => {
|
||||
it("ist ohne coreBase aus und nutzt fetch ohne Sitzung", async () => {
|
||||
const c = createBearerClient("workflow", () => ({}));
|
||||
expect(c.bearerEnabled()).toBe(false);
|
||||
expect(await c.sessionUserId()).toBe("");
|
||||
});
|
||||
it("ist mit coreBase an und liest die Konfiguration bei jedem Aufruf", () => {
|
||||
let base: string | undefined;
|
||||
const c = createBearerClient("workflow", () => ({ coreBase: base }));
|
||||
expect(c.bearerEnabled()).toBe(false);
|
||||
base = "https://core.example";
|
||||
expect(c.bearerEnabled()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { createSession, redirectToLogin, type Session } from "./session";
|
||||
|
||||
export function bearerHeader(request: { headers: { get(name: string): string | null } }): string | null {
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
return auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
}
|
||||
|
||||
export interface BearerClientConfig {
|
||||
coreBase?: string;
|
||||
loginUrl?: string;
|
||||
}
|
||||
|
||||
// config ist eine Funktion, weil NEXT_PUBLIC_*-Werte erst beim Aufruf im Bundle der App stehen.
|
||||
export function createBearerClient(module: string, config: () => BearerClientConfig) {
|
||||
let session: Session | undefined;
|
||||
|
||||
const bearerEnabled = (): boolean => Boolean(config().coreBase);
|
||||
|
||||
function getSession(): Session {
|
||||
if (!session) {
|
||||
const { coreBase, loginUrl } = config();
|
||||
session = createSession({
|
||||
coreBase: coreBase as string,
|
||||
module,
|
||||
onLoginRequired: () => redirectToLogin(loginUrl),
|
||||
});
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
const apiFetch = (input: RequestInfo | URL, init?: RequestInit): Promise<Response> =>
|
||||
bearerEnabled() ? getSession().fetch(input, init) : fetch(input, init);
|
||||
|
||||
async function sessionUserId(): Promise<string> {
|
||||
if (!bearerEnabled()) return "";
|
||||
const s = getSession();
|
||||
await s.token();
|
||||
return s.identity()?.userId ?? "";
|
||||
}
|
||||
|
||||
return { bearerEnabled, getSession, apiFetch, sessionUserId };
|
||||
}
|
||||
@@ -31,6 +31,8 @@ export type { TextFieldProps, SelectFieldProps, CheckboxFieldProps } from "./com
|
||||
|
||||
export { createSession, redirectToLogin, describeHttpError, authMessages, AuthError } from "./auth/session";
|
||||
export type { Session, SessionOptions, Identity, IdentityLoader, AuthErrorKind } from "./auth/session";
|
||||
export { bearerHeader, createBearerClient } from "./auth/bearer";
|
||||
export type { BearerClientConfig } from "./auth/bearer";
|
||||
export { LogoutButton } from "./auth/LogoutButton";
|
||||
export type { LogoutButtonProps } from "./auth/LogoutButton";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// unveraendert durch, damit die Oberflaeche 409 (bereits entschieden) und
|
||||
// 403 (nicht zustaendig) korrekt unterscheiden kann.
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_APPROVALS_API_URL;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Session-Anbindung (auesserhalb dieser Kachel) gibt der Aufrufer sie
|
||||
// als Query-Parameter mit, wie tenantSlug in web/mail-search.
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_APPROVALS_API_URL;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export function bearerHeader(request: NextRequest): string | null {
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
return auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
}
|
||||
@@ -1,29 +1,6 @@
|
||||
import { createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
import { createBearerClient } from "@nexarch/shl";
|
||||
|
||||
let session: Session | undefined;
|
||||
|
||||
export function bearerEnabled(): boolean {
|
||||
return Boolean(process.env.NEXT_PUBLIC_CORE_API_BASE);
|
||||
}
|
||||
|
||||
export function getSession(): Session {
|
||||
if (!session) {
|
||||
session = createSession({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE as string,
|
||||
module: "workflow",
|
||||
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
|
||||
});
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
export function apiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||
return bearerEnabled() ? getSession().fetch(input, init) : fetch(input, init);
|
||||
}
|
||||
|
||||
export async function sessionUserId(): Promise<string> {
|
||||
if (!bearerEnabled()) return "";
|
||||
const s = getSession();
|
||||
await s.token();
|
||||
return s.identity()?.userId ?? "";
|
||||
}
|
||||
export const { bearerEnabled, getSession, apiFetch, sessionUserId } = createBearerClient("workflow", () => ({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE,
|
||||
loginUrl: process.env.NEXT_PUBLIC_LOGIN_URL,
|
||||
}));
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_CASES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_CASES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_CASES_API_URL;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_CASES_API_URL;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export function bearerHeader(request: NextRequest): string | null {
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
return auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
}
|
||||
@@ -1,29 +1,6 @@
|
||||
import { createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
import { createBearerClient } from "@nexarch/shl";
|
||||
|
||||
let session: Session | undefined;
|
||||
|
||||
export function bearerEnabled(): boolean {
|
||||
return Boolean(process.env.NEXT_PUBLIC_CORE_API_BASE);
|
||||
}
|
||||
|
||||
export function getSession(): Session {
|
||||
if (!session) {
|
||||
session = createSession({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE as string,
|
||||
module: "workflow",
|
||||
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
|
||||
});
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
export function apiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||
return bearerEnabled() ? getSession().fetch(input, init) : fetch(input, init);
|
||||
}
|
||||
|
||||
export async function sessionUserId(): Promise<string> {
|
||||
if (!bearerEnabled()) return "";
|
||||
const s = getSession();
|
||||
await s.token();
|
||||
return s.identity()?.userId ?? "";
|
||||
}
|
||||
export const { bearerEnabled, getSession, apiFetch, sessionUserId } = createBearerClient("workflow", () => ({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE,
|
||||
loginUrl: process.env.NEXT_PUBLIC_LOGIN_URL,
|
||||
}));
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_TASKS_API_URL;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// (app/api/case-context/route.ts) als Query-Parameter mitgegeben, bis zu
|
||||
// einer zentralen Session-Anbindung.
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { bearerHeader } from "../../../lib/forward";
|
||||
import { bearerHeader } from "@nexarch/shl/auth/bearer";
|
||||
|
||||
function backendURL(): string {
|
||||
const base = process.env.WORKFLOW_TASKS_API_URL;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
export function bearerHeader(request: NextRequest): string | null {
|
||||
const auth = request.headers.get("authorization") ?? "";
|
||||
return auth.startsWith("Bearer ") && auth.length > 7 ? auth : null;
|
||||
}
|
||||
@@ -1,29 +1,6 @@
|
||||
import { createSession, redirectToLogin, type Session } from "@nexarch/shl";
|
||||
import { createBearerClient } from "@nexarch/shl";
|
||||
|
||||
let session: Session | undefined;
|
||||
|
||||
export function bearerEnabled(): boolean {
|
||||
return Boolean(process.env.NEXT_PUBLIC_CORE_API_BASE);
|
||||
}
|
||||
|
||||
export function getSession(): Session {
|
||||
if (!session) {
|
||||
session = createSession({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE as string,
|
||||
module: "workflow",
|
||||
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
|
||||
});
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
export function apiFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||
return bearerEnabled() ? getSession().fetch(input, init) : fetch(input, init);
|
||||
}
|
||||
|
||||
export async function sessionUserId(): Promise<string> {
|
||||
if (!bearerEnabled()) return "";
|
||||
const s = getSession();
|
||||
await s.token();
|
||||
return s.identity()?.userId ?? "";
|
||||
}
|
||||
export const { bearerEnabled, getSession, apiFetch, sessionUserId } = createBearerClient("workflow", () => ({
|
||||
coreBase: process.env.NEXT_PUBLIC_CORE_API_BASE,
|
||||
loginUrl: process.env.NEXT_PUBLIC_LOGIN_URL,
|
||||
}));
|
||||
Reference in new issue
Block a user