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
11 changes: 11 additions & 0 deletions apps/docs/users.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ Operators can also configure an email allowlist for a self-hosted deployment.
When that allowlist is active, a user still needs to pass the normal sign-in
rules and have an allowed email address.

## Staying signed in

Web sign-in sessions last 30 days from sign-in or the last renewal. While you
use the web app, eligible browser requests renew both the session and its
cookie, at most once every 24 hours. Simply leaving a sleeping or closed browser
open does not renew a session. After the session expires, sign in again.

Signing out, removal by an admin, and password resets still revoke sessions.
Clearing browser cookies or rotating the deployment's session-signing secret
also requires signing in again. Sign out when using a shared device.

## License and seats

A Roomote deployment is free for up to 10 users. Every registered user
Expand Down
8 changes: 8 additions & 0 deletions apps/web/src/app/api/trpc/[trpc]/__tests__/route.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/web/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const handler = async (req: Request) => {
const contextStartedAt = performance.now();

try {
return await createContext();
return await createContext({ allowSessionRefresh: true });
} finally {
authMs = performance.now() - contextStartedAt;
}
Expand Down
16 changes: 16 additions & 0 deletions apps/web/src/lib/server/auth-context.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 12 additions & 5 deletions apps/web/src/lib/server/auth-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type SignedInAuthContext = {

type SignedInAuthContextOptions = {
treatPendingAsSignedOut?: boolean;
/** Only enable in a Route Handler that can send renewed cookies. */
allowSessionRefresh?: boolean;
};

async function loadDeploymentIdentityState(userId: string) {
Expand Down Expand Up @@ -314,12 +316,15 @@ function isMatchingUserEntity(
);
}

async function getBetterAuthSession() {
async function getBetterAuthSession(allowSessionRefresh = false) {
const auth = await getAuth();

try {
return await auth.api.getSession({
headers: await headers(),
// Rendering cannot write cookies. Renewing only the database here would
// prevent a later browser request from renewing the cookie for 24 hours.
query: { disableRefresh: !allowSessionRefresh },
});
} catch (error) {
// Invalid or expired auth cookies should behave like a signed-out
Expand All @@ -333,11 +338,11 @@ async function getBetterAuthSession() {
}

export async function getSignedInAuthContext(
_options?: SignedInAuthContextOptions,
options?: SignedInAuthContextOptions,
): Promise<SignedInAuthContext | AuthError> {
await bootstrapWebRuntimeEnv();

const session = await getBetterAuthSession();
const session = await getBetterAuthSession(options?.allowSessionRefresh);

if (!session) {
return { success: false, error: 'Unauthorized: User required' };
Expand Down Expand Up @@ -414,8 +419,10 @@ export async function getSignedInAuthContext(
};
}

export async function authorize(): Promise<UserAuthSuccess | AuthError> {
const authContext = await getSignedInAuthContext();
export async function authorize(
options?: SignedInAuthContextOptions,
): Promise<UserAuthSuccess | AuthError> {
const authContext = await getSignedInAuthContext(options);

if (!authContext.success) {
return authContext;
Expand Down
238 changes: 238 additions & 0 deletions apps/web/src/lib/server/auth-session.integration.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions apps/web/src/lib/server/auth.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion apps/web/src/lib/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ type AuthSessionResult = {

type RoomoteAuth = {
api: {
getSession(input: { headers: Headers }): Promise<AuthSessionResult>;
getSession(input: {
headers: Headers;
query?: { disableRefresh?: boolean };
}): Promise<AuthSessionResult>;
requestPasswordReset(input: {
body: {
email: string;
Expand Down Expand Up @@ -1044,6 +1047,8 @@ async function createAuth(authProviderConfig: ResolvedAuthProviderConfig) {
},
session: {
modelName: 'authSessions',
expiresIn: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
// Better Auth gates account unlinking (and similar operations) behind a
// "fresh session" check that defaults to one day, which makes
// Settings > Linked Accounts unlink fail with "Session is not fresh"
Expand Down
Loading
Loading