fix: use headscale.api_key where possible

This commit is contained in:
Aarnav Tale
2026-04-03 16:34:50 -04:00
parent 73b5d5514e
commit 4cd0c1e206
17 changed files with 23 additions and 32 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ export async function loader({ request, context, ...rest }: Route.LoaderArgs) {
try {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const user =
+1 -1
View File
@@ -26,7 +26,7 @@ export async function aclAction({ request, context }: Route.ActionArgs) {
});
}
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
try {
const { policy, updatedAt } = await api.setPolicy(policyData);
+1 -1
View File
@@ -29,7 +29,7 @@ export async function aclLoader({ request, context }: Route.LoaderArgs) {
};
// Try to load the ACL policy from the API.
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
try {
const { policy, updatedAt } = await api.getPolicy();
+1 -1
View File
@@ -107,7 +107,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
});
try {
const hsApi = context.hsApi.getRuntimeClient(context.oidc!.apiKey);
const hsApi = context.hsApi.getRuntimeClient(context.headscaleApiKey!);
const hsUsers = await hsApi.getUsers();
const hsUser = findHeadscaleUserBySubject(hsUsers, claims.sub, userInfo.email);
if (hsUser) {
+2 -2
View File
@@ -28,7 +28,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
principal.kind === "oidc" &&
!principal.user.headscaleUserId
) {
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
let headscaleUsers: { id: string; name: string }[] = [];
@@ -64,7 +64,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
}
// No UI access — show the download/connect page
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
let linkedUserName: string | undefined;
+1 -3
View File
@@ -10,9 +10,7 @@ export async function machineAction({ request, context }: Route.ActionArgs) {
const principal = await context.auth.require(request);
const formData = await request.formData();
const api = context.hsApi.getRuntimeClient(
context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey),
);
const api = context.hsApi.getRuntimeClient(context.auth.getHeadscaleApiKey(principal));
const action = formData.get("action_id")?.toString();
if (!action) {
+1 -3
View File
@@ -38,9 +38,7 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
}
}
const api = context.hsApi.getRuntimeClient(
context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey),
);
const api = context.hsApi.getRuntimeClient(context.auth.getHeadscaleApiKey(principal));
const [nodesSnap, usersSnap] = await Promise.all([
context.hsLive.get(nodesResource, api),
context.hsLive.get(usersResource, api),
+1 -3
View File
@@ -30,9 +30,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
const writablePermission = context.auth.can(principal, Capabilities.write_machines);
const api = context.hsApi.getRuntimeClient(
context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey),
);
const api = context.hsApi.getRuntimeClient(context.auth.getHeadscaleApiKey(principal));
const [nodesSnap, usersSnap] = await Promise.all([
context.hsLive.get(nodesResource, api),
context.hsLive.get(usersResource, api),
+1 -1
View File
@@ -7,7 +7,7 @@ import type { Route } from "./+types/overview";
export async function authKeysAction({ request, context }: Route.ActionArgs) {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const canGenerateAny = context.auth.can(principal, Capabilities.generate_authkeys);
+1 -1
View File
@@ -20,7 +20,7 @@ import AddAuthKey from "./dialogs/add-auth-key";
export async function loader({ request, context }: Route.LoaderArgs) {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const usersSnap = await context.hsLive.get(usersResource, api);
+1 -1
View File
@@ -41,7 +41,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
throw data("Only OAuth users are allowed to use WebSSH", 403);
}
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const users = await api.getUsers();
+1 -1
View File
@@ -54,7 +54,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
let apiError: string | undefined;
try {
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const [nodesSnap, usersSnap] = await Promise.all([
context.hsLive.get(nodesResource, api),
+1 -1
View File
@@ -24,7 +24,7 @@ export async function userAction({ request, context }: Route.ActionArgs) {
});
}
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
switch (action) {
case "create_user": {
+1 -1
View File
@@ -5,7 +5,7 @@ import type { Route } from "./+types/live";
export async function loader({ request, context }: Route.LoaderArgs) {
const principal = await context.auth.require(request);
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
// Ensure resources are loaded before streaming
+1 -1
View File
@@ -18,7 +18,7 @@ export async function pruneEphemeralNodes({ context, request }: Route.LoaderArgs
return;
}
const apiKey = context.auth.getHeadscaleApiKey(principal, context.oidc?.apiKey);
const apiKey = context.auth.getHeadscaleApiKey(principal);
const api = context.hsApi.getRuntimeClient(apiKey);
const nodes = await api.getNodes();
const toPrune = nodes.filter((node) => {
+2 -1
View File
@@ -84,6 +84,7 @@ const appLoadContext = {
auth: createAuthService({
secret: config.server.cookie_secret,
headscaleApiKey,
db,
cookie: {
name: "_hp_auth",
@@ -93,13 +94,13 @@ const appLoadContext = {
},
}),
headscaleApiKey,
hsApi,
agents,
integration: await loadIntegration(config.integration),
oidc:
config.oidc && config.oidc.enabled !== false && headscaleApiKey
? {
apiKey: headscaleApiKey,
connector: createLazyOidcConnector(
config.server.base_url,
config.oidc,
+5 -9
View File
@@ -58,6 +58,7 @@ interface CookiePayload {
export interface AuthServiceOptions {
secret: string;
headscaleApiKey?: string;
db: NodeSQLiteDatabase;
cookie: {
name: string;
@@ -230,21 +231,16 @@ export class AuthService {
return this.encodeCookie({ sid, api_key: apiKey }, Math.floor(maxAge / 1000));
}
/**
* Get the Headscale API key for making API calls.
* OIDC sessions use the configured oidc.headscale_api_key.
* API key sessions use the user-provided key stored in the cookie.
*/
getHeadscaleApiKey(principal: Principal, oidcApiKey?: string): string {
getHeadscaleApiKey(principal: Principal): string {
if (principal.kind === "api_key") {
return principal.apiKey;
}
if (!oidcApiKey) {
throw new Error("OIDC sessions require oidc.headscale_api_key");
if (!this.opts.headscaleApiKey) {
throw new Error("OIDC sessions require headscale.api_key to be configured");
}
return oidcApiKey;
return this.opts.headscaleApiKey;
}
/**