mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
change oidc behavior with remember me on by default when oidc only enabled (#2397)
This commit is contained in:
@@ -498,7 +498,9 @@ export default function LoginPage(): React.ReactElement {
|
||||
)}
|
||||
<a
|
||||
onClick={clearSignedOut}
|
||||
href={`/api/auth/oidc/login${inviteToken ? '?invite=' + encodeURIComponent(inviteToken) : ''}`}
|
||||
// No remember-me switch in OIDC-only mode: the IdP owns the session
|
||||
// policy, so always ask for the remembered lifetime (#1927).
|
||||
href={`/api/auth/oidc/login${inviteToken ? '?invite=' + encodeURIComponent(inviteToken) + '&' : '?'}remember=1`}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '12px',
|
||||
|
||||
@@ -203,7 +203,8 @@ describe('LoginPage — OIDC-only mode', () => {
|
||||
|
||||
expect(screen.queryByPlaceholderText(EMAIL_PLACEHOLDER)).toBeNull();
|
||||
const link = screen.getByRole('link', { name: /sign in with authentik/i });
|
||||
expect(link).toHaveAttribute('href', '/api/auth/oidc/login');
|
||||
// No switch in OIDC-only mode: the link always asks for the remembered lifetime.
|
||||
expect(link).toHaveAttribute('href', '/api/auth/oidc/login?remember=1');
|
||||
expect(
|
||||
screen.getByText('Password authentication is disabled. Please sign in using your SSO provider.'),
|
||||
).toBeInTheDocument();
|
||||
@@ -222,7 +223,7 @@ describe('LoginPage — OIDC-only mode', () => {
|
||||
expect(screen.getByText('Invalid session. Please try again.')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /sign in with sso/i })).toHaveAttribute(
|
||||
'href',
|
||||
'/api/auth/oidc/login?invite=inv%2F42',
|
||||
'/api/auth/oidc/login?invite=inv%2F42&remember=1',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -180,7 +180,9 @@ describe('useLogin — app config probe', () => {
|
||||
await ready(result);
|
||||
|
||||
expect(result.current.oidcOnly).toBe(true);
|
||||
await waitFor(() => expect(window.location.href).toBe('/api/auth/oidc/login'));
|
||||
// OIDC-only has no remember-me switch and the IdP owns the session policy,
|
||||
// so the auto-redirect always asks for the remembered lifetime.
|
||||
await waitFor(() => expect(window.location.href).toBe('/api/auth/oidc/login?remember=1'));
|
||||
});
|
||||
|
||||
it('FE-LOGIN-HOOK-008: does not bounce back to the IdP right after a logout', async () => {
|
||||
|
||||
@@ -186,7 +186,9 @@ export function useLogin() {
|
||||
// Skip auto-redirect when config is from cache — network is unreliable
|
||||
// and auto-redirecting to the IdP could loop if the proxy changed.
|
||||
if (!fromCache && !config.password_login && config.oidc_login && config.oidc_configured && config.has_users && !invite && !noRedirect) {
|
||||
window.location.href = '/api/auth/oidc/login'
|
||||
// No switch to consult on this path: OIDC-only always asks for the
|
||||
// remembered lifetime, matching the SSO button on the panel (#1927).
|
||||
window.location.href = '/api/auth/oidc/login?remember=1'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -166,7 +166,10 @@ export class OidcController {
|
||||
if ('error' in result) return f('/login?oidc_error=' + result.error);
|
||||
|
||||
this.oidc.touchLastLogin(result.user.id);
|
||||
const jwtToken = this.oidc.generateToken(result.user, pending.remember === true);
|
||||
// Pass the flag through untouched: `undefined` must reach the token as
|
||||
// "absent", not `false`, or the sliding renewal would later downgrade the
|
||||
// default persistent cookie to a browser-session one (remember-me, #1927).
|
||||
const jwtToken = this.oidc.generateToken(result.user, pending.remember);
|
||||
const { code: authCode, binding } = this.oidc.createAuthCode(jwtToken, pending.remember);
|
||||
// Bind the code to THIS browser, the way the state cookie binds the callback.
|
||||
// The code rides home in a URL, so it is readable from history, from a
|
||||
|
||||
@@ -429,6 +429,12 @@ describe('OIDC remember-me (#1927)', () => {
|
||||
const res = await runFlow('', 'sub-rm-abs', 'rmabs@example.com');
|
||||
expect(res.status).toBe(200);
|
||||
expect(sessionCookie(res)).toContain('Max-Age=86400');
|
||||
// The JWT must not carry `remember: false` either — the sliding renewal
|
||||
// re-issues the cookie from that claim, and `false` would turn the
|
||||
// persistent default into a browser-session cookie half a day later.
|
||||
const jwt = require('jsonwebtoken');
|
||||
const decoded = jwt.decode(res.body.token) as { remember?: boolean };
|
||||
expect(decoded.remember).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -358,12 +358,11 @@ describe('OidcController /callback', () => {
|
||||
expect(res.redirectedTo).toBe('https://app/login?oidc_error=server_error');
|
||||
});
|
||||
|
||||
it('threads the pending remember flag into generateToken and createAuthCode', async () => {
|
||||
for (const [remember, expectedGenerate] of [
|
||||
[true, true],
|
||||
[false, false],
|
||||
[undefined, false],
|
||||
] as const) {
|
||||
it('threads the pending remember flag unchanged into generateToken and createAuthCode', async () => {
|
||||
// An absent flag must stay absent in the token: coercing it to `false`
|
||||
// would let the sliding renewal downgrade the default persistent cookie
|
||||
// to a browser-session cookie half a lifetime later.
|
||||
for (const remember of [true, false, undefined] as const) {
|
||||
const generateToken = vi.fn().mockReturnValue('jwt');
|
||||
const createAuthCode = vi.fn().mockReturnValue({ code: 'ac', binding: 'bnd' });
|
||||
const res = makeRes();
|
||||
@@ -376,7 +375,7 @@ describe('OidcController /callback', () => {
|
||||
generateToken,
|
||||
createAuthCode,
|
||||
})).callback('c', 's', undefined, reqCb('s'), res);
|
||||
expect(generateToken).toHaveBeenCalledWith({ id: 1 }, expectedGenerate);
|
||||
expect(generateToken).toHaveBeenCalledWith({ id: 1 }, remember);
|
||||
expect(createAuthCode).toHaveBeenCalledWith('jwt', remember);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,6 +24,8 @@ OpenID Connect (OIDC) lets users log in with an existing identity provider — G
|
||||
| `0` | `SESSION_DURATION` (default `24h`) | browser-session cookie, cleared when the browser closes |
|
||||
| omitted | `SESSION_DURATION` (default `24h`) | persistent, `maxAge` matches |
|
||||
|
||||
In OIDC-only mode (password login disabled) there is no switch: both the SSO button and the automatic redirect to the provider always send `remember=1`, so SSO sessions get the `SESSION_DURATION_REMEMBER` lifetime. Tune that variable if you want shorter sessions on an OIDC-only instance.
|
||||
|
||||
If SSO sessions are dying at browser close, the login link is sending `remember=0`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Reference in New Issue
Block a user