Skip to content
Open
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
18 changes: 5 additions & 13 deletions adminforth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,19 @@ class AdminForthAuth implements IAdminForthAuth {
response.setHeader('Set-Cookie', `adminforth_${brandSlug}_jwt=; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
}

setAuthCookie({ expireInDays, response, username, pk}: {
expireInDays?: number,
setAuthCookie({ expireInDuration, response, username, pk}: {
expireInDuration?: string,
response: any,
username: string,
pk: string | null
}) {
console.log("in days", expireInDays);
const expiresIn: string = expireInDays ? `${expireInDays}d` : (process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h');
console.log("in string", expiresIn);
const expiresIn: string = expireInDuration || (process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h');
// might be h,m,d in string

const expiresInSec = parseTimeToSeconds(expiresIn);

console.log("expiresInSec", expiresInSec);

const token = this.issueJWT({ username, pk}, 'auth', expiresIn);
console.log("token", token);
const token = this.issueJWT({ username, pk}, 'auth', expiresInSec);
const expiresCookieFormat = new Date(Date.now() + expiresInSec * 1000).toUTCString();
console.log("expiresCookieFormat", expiresCookieFormat);
const brandSlug = this.adminforth.config.customization.brandNameSlug;
console.log("brandSlug", brandSlug);
response.setHeader('Set-Cookie', `adminforth_${brandSlug}_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${expiresCookieFormat}`);
}

Expand Down Expand Up @@ -131,7 +123,7 @@ class AdminForthAuth implements IAdminForthAuth {
return cookies.find((cookie) => cookie.key === `adminforth_${brandSlug}_${name}`)?.value || null;
}

issueJWT(payload: Object, type: string, expiresIn: string = '24h'): string {
issueJWT(payload: Object, type: string, expiresIn: string | number = '24h'): string {
// read ADMINFORH_SECRET from environment if not drop error
const secret = process.env.ADMINFORTH_SECRET;
if (!secret) {
Expand Down
2 changes: 1 addition & 1 deletion adminforth/commands/createApp/templates/index.ts.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const admin = new AdminForth({
usersResourceId: 'adminuser',
usernameField: 'email',
passwordHashField: 'password_hash',
rememberMeDays: 30,
rememberMeDuration: '30d',
loginBackgroundImage: 'https://images.unsplash.com/photo-1534239697798-120952b76f2b?q=80&w=3389&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
loginBackgroundPosition: '1/2',
loginPromptHTML: async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ You can tweak login cookie expiration time by setting environment `ADMINFORTH_AU
ADMINFORTH_AUTH_EXPIRESIN=1h
```

Also you can set `auth.rememberMeDays` in the config to set how long "remember me" logins will last.
Also you can set `auth.rememberMeDuration` in the config to set how long "remember me" logins will last.
For example to set it to 7 days:

```ts ./index.ts
new AdminForth({
...
auth: {
rememberMeDays: 7
rememberMeDuration: '7d' // '7d' for 7 days, '24h' for 24 hours, '30m' for 30 minutes, etc.
}
}
```
Expand Down
27 changes: 27 additions & 0 deletions adminforth/modules/configValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,33 @@ export default class ConfigValidator implements IConfigValidator {
}
}

if (newConfig.auth.rememberMeDays !== undefined) {
const rememberMeDays = newConfig.auth.rememberMeDays;
if (typeof rememberMeDays !== 'number' || rememberMeDays <= 0) {
errors.push(`auth.rememberMeDays must be a positive number`);
} else {
if (!newConfig.auth.rememberMeDuration) {
newConfig.auth.rememberMeDuration = `${rememberMeDays}d`;
warnings.push(`⚠️ auth.rememberMeDays is deprecated. Please use auth.rememberMeDuration: "${rememberMeDays}d" instead. Auto-converted for now.`);
} else {
warnings.push(`⚠️ Both auth.rememberMeDays and auth.rememberMeDuration are set. Using rememberMeDuration. Please remove rememberMeDays.`);
}
}
delete newConfig.auth.rememberMeDays;
}

if (newConfig.auth.rememberMeDuration !== undefined) {
const duration = newConfig.auth.rememberMeDuration;
if (typeof duration !== 'string') {
errors.push(`auth.rememberMeDuration must be a string in format "1s", "1m", "1h", or "1d"`);
} else {
const match = duration.match(/^(\d+)([smhd])$/);
if (!match) {
errors.push(`auth.rememberMeDuration must be in format "1s", "1m", "1h", or "1d" (e.g., "30d" for 30 days), got: "${duration}"`);
}
}
}

// normalize beforeLoginConfirmation hooks
const blc = this.inputConfig.auth.beforeLoginConfirmation;
if (!Array.isArray(blc)) {
Expand Down
18 changes: 10 additions & 8 deletions adminforth/modules/restApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
this.adminforth = adminforth;
}

async processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin:boolean, error?: string }, response: any, extra: HttpExtra, rememberMeDays?: number) {
async processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin:boolean, error?: string }, response: any, extra: HttpExtra, sessionDuration?: string) {
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);

for (const hook of listify(beforeLoginConfirmation)) {
Expand All @@ -138,7 +138,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
response,
adminforth: this.adminforth,
extra,
rememberMeDays
sessionDuration,
});

if (resp?.body?.redirectTo || resp?.error) {
Expand Down Expand Up @@ -205,17 +205,19 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
username,
};

const expireInDays = rememberMe ? this.adminforth.config.auth.rememberMeDays || 30 : 1;

const expireInDuration = rememberMe
? (this.adminforth.config.auth.rememberMeDuration || '30d')
: '1d';
console.log('expireInDuration', expireInDuration);

await this.processLoginCallbacks(adminUser, toReturn, response, {
body, headers, query, cookies, requestUrl,
}, expireInDays);
}, expireInDuration);

if (toReturn.allowedLogin) {

this.adminforth.auth.setAuthCookie({
expireInDays,
expireInDuration,
response,
username,
pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
Expand Down Expand Up @@ -289,7 +291,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
everyPageBottom: this.adminforth.config.customization.globalInjections.everyPageBottom,
sidebarTop: this.adminforth.config.customization.globalInjections.sidebarTop,
},
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
rememberMeDuration: this.adminforth.config.auth.rememberMeDuration,
singleTheme: this.adminforth.config.customization.singleTheme,
customHeadItems: this.adminforth.config.customization.customHeadItems,
};
Expand Down Expand Up @@ -381,7 +383,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
title: this.adminforth.config.customization?.title,
demoCredentials: this.adminforth.config.auth.demoCredentials,
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
rememberMeDuration: this.adminforth.config.auth.rememberMeDuration,
singleTheme: this.adminforth.config.customization.singleTheme,
customHeadItems: this.adminforth.config.customization.customHeadItems,
}
Expand Down
4 changes: 2 additions & 2 deletions adminforth/spa/src/views/LoginView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
</Input>
</div>

<div v-if="coreStore.config.rememberMeDays"
<div v-if="coreStore.config.rememberMeDuration"
class="flex items-start mb-5"
:title="$t(`Stay logged in for {days} days`, {days: coreStore.config.rememberMeDays})"
:title="$t(`Stay logged in for {days}`, {days: coreStore.config.rememberMeDuration})"
>
<Checkbox v-model="rememberMeValue" class="mr-2">
{{ $t('Remember me') }}
Expand Down
19 changes: 11 additions & 8 deletions adminforth/types/Back.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,15 @@ export interface IAdminForthDataSourceConnectorConstructor {
export interface IAdminForthAuth {
verify(jwt : string, mustHaveType: string, decodeUser?: boolean): Promise<any>;

issueJWT(payload: Object, type: string, expiresIn?: string): string;
issueJWT(payload: Object, type: string, expiresIn?: string | number): string;

removeCustomCookie({response, name}: {response: any, name: string}): void;

setCustomCookie({response, payload}: {response: any, payload: {name: string, value: string, expiry: number, expirySeconds: number, httpOnly: boolean}}): void;

getCustomCookie({cookies, name}: {cookies: {key: string, value: string}[], name: string}): string | null;

setAuthCookie({expireInDays, response, username, pk,}: {expireInDays?: number, response: any, username: string, pk: string}): void;
setAuthCookie({expireInDuration, response, username, pk,}: {expireInDuration?: string, response: any, username: string, pk: string}): void;

removeAuthCookie(response: any): void;

Expand All @@ -331,8 +331,9 @@ export interface IAdminForthRestAPI {
* @param adminUser - plugin/af pases current adminUser
* @param toReturn - this is an object which will get status of login process. If at least one callback returns error or redirectTo, login process will be stopped (future callbacks will not be called).
* @param response - http response object
* @param sessionDuration - duration of session in format "1s", "1m", "1h", or "1d" (e.g., "30d" for 30 days)
*/
processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin: boolean, error?: string }, response: any, extra: HttpExtra): Promise<void>;
processLoginCallbacks(adminUser: AdminUser, toReturn: { redirectTo?: string, allowedLogin: boolean, error?: string }, response: any, extra: HttpExtra, sessionDuration?: string): Promise<void>;
}

export interface IAdminForth {
Expand Down Expand Up @@ -605,7 +606,7 @@ export type BeforeLoginConfirmationFunction = (params?: {
response: IAdminForthHttpResponse,
adminforth: IAdminForth,
extra?: HttpExtra,
rememberMeDays?: number,
sessionDuration?: string,
}) => Promise<{
error?: string,
body: {
Expand Down Expand Up @@ -1053,11 +1054,13 @@ export interface AdminForthInputConfig {
loginPromptHTML?: string | (() => string | void | undefined | Promise<string | void | undefined>) | undefined

/**
* Remember me days for "Remember Me" checkbox on login page.
* If not set or set to null/0/undefined, "Remember Me" checkbox will not be displayed.
* If rememberMeDays is set, then users who check "Remember Me" will be staying logged in for this amount of days.
* Remember me duration for "Remember Me" checkbox on login page.
* If not set or set to null/undefined, "Remember Me" checkbox will not be displayed.
* If rememberMeDuration is set, then users who check "Remember Me" will be staying logged in for this amount of time.
* Format: "1s" (seconds), "1m" (minutes), "1h" (hours), or "1d" (days).
* Example: "30d" for 30 days, "7d" for 7 days, "24h" for 24 hours.
*/
rememberMeDays?: number,
rememberMeDuration?: string,


/**
Expand Down
2 changes: 1 addition & 1 deletion adminforth/types/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ export interface AdminForthConfigForFrontend {
underInputs: Array<AdminForthComponentDeclaration>,
panelHeader: Array<AdminForthComponentDeclaration>,
},
rememberMeDays: number,
rememberMeDuration: string,
showBrandNameInSidebar: boolean,
showBrandLogoInSidebar: boolean,
brandLogo?: string,
Expand Down