Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/auth/keycloak/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ const defaults = {
},
backchannelLogout: {
endpoint: '/.oidc/backchannel-logout'
}
},
allowInsecureRequests: false
} satisfies OidcConfig;
```

Expand All @@ -119,6 +120,7 @@ Important options:
- `signin.*` and `signout.*`: Paths for the pages/endpoints used during the flow
- `session.endpoint`: Path for the [Session Management](#reacting-to-a-session-change-at-keycloak-openid-connect-session-management-10) data endpoint
- `backchannelLogout.endpoint`: Path Keycloak POSTs Logout Tokens to for [Back-Channel Logout](#reacting-to-a-session-change-when-no-browser-tab-is-open-openid-connect-back-channel-logout-10)
- `allowInsecureRequests`: Set to `true` to allow a plain-HTTP `issuer` (e.g. `http://localhost:8080/realms/dev`) - `openid-client`/`oauth4webapi` reject non-HTTPS issuers outright otherwise. Off by default; only enable it for local development, never in an environment `issuer` could plausibly point somewhere real.
- Session options from `@escendit/sveltekit-session`: `cookie`, `expireIn`, `size`, `sessionStore`, `sessionHasher`, `sessionGenerator`

Overriding the session/backchannel-logout endpoint paths:
Expand Down Expand Up @@ -270,6 +272,7 @@ Two things worth knowing about the current scope:
- `issuer mismatched` errors: Verify `KEYCLOAK_ISSUER` matches the realm’s issuer exactly.
- Cookies not set locally: Use `http://localhost` and ensure you’re not mixing `http` and `https`. Also check the session cookie name and domain.
- Unreachable or misconfigured `issuer`: OIDC discovery runs once when `OidcMiddleware` is constructed and is reused for every request. If it fails (unreachable issuer, DNS failure, etc.) the server itself keeps running rather than crashing. Sign-in and token refresh, which need the discovered configuration, fail per-request. Sign-out degrades gracefully instead: the local session is still cleared, and the user is redirected directly to `redirect_uri` (skipping the Keycloak round trip) rather than the request failing.
- Local Keycloak on plain HTTP (e.g. `http://localhost:8080/realms/dev`) fails discovery with a generic-looking error: set `allowInsecureRequests: true` (see Configuration above) - without it, `openid-client` rejects non-HTTPS issuers outright.

## Related

Expand Down
1 change: 1 addition & 0 deletions packages/auth/keycloak/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const Defaults: InternalOidcConfig = {
},
clientId: "invalid-client",
clientSecret: "invalid-secret",
allowInsecureRequests: false,
};

export {
Expand Down
30 changes: 30 additions & 0 deletions packages/auth/keycloak/src/lib/middleware.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,3 +735,33 @@ describe('Back-Channel Logout', () => {
expect(response.status).toBe(503);
});
});

describe('allowInsecureRequests', () => {
beforeEach(() => {
vi.mocked(client.discovery).mockClear();
});

it('does not pass an execute option to discovery() by default', () => {
OidcMiddleware(baseConfig());

expect(client.discovery).toHaveBeenCalledWith(
expect.any(URL),
expect.any(String),
expect.any(String),
undefined,
undefined
);
});
Comment thread
sourcery-ai[bot] marked this conversation as resolved.

it('passes execute: [allowInsecureRequests] to discovery() when explicitly enabled', () => {
OidcMiddleware({ ...baseConfig(), allowInsecureRequests: true });

expect(client.discovery).toHaveBeenCalledWith(
expect.any(URL),
expect.any(String),
expect.any(String),
undefined,
{ execute: [client.allowInsecureRequests] }
);
});
});
15 changes: 15 additions & 0 deletions packages/auth/keycloak/src/lib/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ const OidcMiddleware: Middleware = (config?: OidcConfig): Handle => {
configuredConfig.clientSecret = config.clientSecret;
}

if (config?.allowInsecureRequests !== undefined) {
configuredConfig.allowInsecureRequests = config.allowInsecureRequests;
}

const errors = ValidateOidcConfiguration(configuredConfig);

if (errors.length > 0) {
Expand All @@ -281,10 +285,21 @@ const OidcMiddleware: Middleware = (config?: OidcConfig): Handle => {

// Discover once per middleware instance and reuse for every request rather than
// rediscovering (a network round-trip) on each one.
//
// openid-client/oauth4webapi reject non-HTTPS issuers outright unless execute:
// [client.allowInsecureRequests] is explicitly passed here - it's not just a discovery
// flag, it also carries through to every subsequent request made with the resulting
// Configuration. Only opt in when allowInsecureRequests is explicitly set (e.g. for a
// local http://localhost Keycloak); the 4th positional argument (clientAuthentication)
// is left at its default by passing undefined.
configuredConfig.oidcConfiguration = client.discovery(
new URL(configuredConfig.issuer),
configuredConfig.clientId,
configuredConfig.clientSecret,
undefined,
configuredConfig.allowInsecureRequests === true
? {execute: [client.allowInsecureRequests]}
: undefined,
);

// Nothing awaits this promise synchronously at construction time, so an unreachable
Expand Down
8 changes: 8 additions & 0 deletions packages/auth/keycloak/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ type OidcConfig = SessionConfig & {
issuer?: string;
clientId?: string;
clientSecret?: string;
/**
* Allows plain-HTTP OIDC issuers (openid-client/oauth4webapi otherwise reject
* non-HTTPS issuers outright during discovery). Off by default - only turn this on for
* local development against an issuer like `http://localhost:8080/realms/dev`, never in
* an environment `issuer` could plausibly point somewhere real.
*/
allowInsecureRequests?: boolean;
};

/**
Expand Down Expand Up @@ -85,6 +92,7 @@ type InternalOidcConfig = InternalSessionConfig & {
issuer: string;
clientId: string;
clientSecret: string;
allowInsecureRequests: boolean;
/**
* Discovered openid-client Configuration, resolved once when the middleware is
* constructed and reused for every request rather than rediscovering per-request.
Expand Down
Loading