@wbce-d9/api 10.3.1 → 10.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +6 -4
- package/dist/auth/drivers/saml.js +3 -3
- package/dist/cli/utils/create-env/env-stub.liquid +3 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +4 -0
- package/dist/controllers/auth.js +6 -3
- package/dist/database/run-ast.js +1 -1
- package/dist/env.js +2 -0
- package/dist/middleware/authenticate.js +15 -1
- package/dist/services/authentication.js +20 -18
- package/dist/services/graphql/index.js +3 -3
- package/dist/services/payload.d.ts +2 -0
- package/dist/services/payload.js +9 -1
- package/dist/services/shares.js +12 -11
- package/dist/types/auth.d.ts +1 -0
- package/dist/utils/jwt.js +3 -3
- package/package.json +13 -13
package/dist/app.js
CHANGED
|
@@ -108,10 +108,12 @@ export default async function createApp() {
|
|
|
108
108
|
await emitter.emitInit('app.before', { app });
|
|
109
109
|
await emitter.emitInit('middlewares.before', { app });
|
|
110
110
|
app.use(expressLogger);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
if (env['X_POWERED_BY_ENABLED'] === true) {
|
|
112
|
+
app.use((_req, res, next) => {
|
|
113
|
+
res.setHeader('X-Powered-By', 'Directus');
|
|
114
|
+
next();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
115
117
|
if (env['CORS_ENABLED'] === true) {
|
|
116
118
|
app.use(cors);
|
|
117
119
|
}
|
|
@@ -3,7 +3,7 @@ import { BaseException } from '@wbce-d9/exceptions';
|
|
|
3
3
|
import express, { Router } from 'express';
|
|
4
4
|
import * as samlify from 'samlify';
|
|
5
5
|
import { getAuthProvider } from '../../auth.js';
|
|
6
|
-
import { ACCESS_COOKIE_OPTIONS, REFRESH_COOKIE_OPTIONS } from '../../constants.js';
|
|
6
|
+
import { ACCESS_COOKIE_CLEAR_OPTIONS, ACCESS_COOKIE_OPTIONS, REFRESH_COOKIE_CLEAR_OPTIONS, REFRESH_COOKIE_OPTIONS, } from '../../constants.js';
|
|
7
7
|
import getDatabase from '../../database/index.js';
|
|
8
8
|
import emitter from '../../emitter.js';
|
|
9
9
|
import env from '../../env.js';
|
|
@@ -103,13 +103,13 @@ export function createSAMLAuthRouter(providerName) {
|
|
|
103
103
|
const { context } = sp.createLogoutRequest(idp, 'redirect', req.body);
|
|
104
104
|
const authService = new AuthenticationService({ accountability: req.accountability, schema: req.schema });
|
|
105
105
|
if (req.cookies[env['ACCESS_TOKEN_COOKIE_NAME']]) {
|
|
106
|
-
res.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'],
|
|
106
|
+
res.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'], ACCESS_COOKIE_CLEAR_OPTIONS);
|
|
107
107
|
}
|
|
108
108
|
if (req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']]) {
|
|
109
109
|
const currentRefreshToken = req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
|
110
110
|
if (currentRefreshToken) {
|
|
111
111
|
await authService.logout(currentRefreshToken);
|
|
112
|
-
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'],
|
|
112
|
+
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'], REFRESH_COOKIE_CLEAR_OPTIONS);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
return res.redirect(context);
|
|
@@ -230,6 +230,9 @@ REFRESH_TOKEN_COOKIE_NAME="directus_refresh_token"
|
|
|
230
230
|
# and it should be greater than the time taken for a login request with an invalid password [500]
|
|
231
231
|
# LOGIN_STALL_TIME=500
|
|
232
232
|
|
|
233
|
+
# Whether or not to enable the X-Powered-By Header [true]
|
|
234
|
+
X_POWERED_BY_ENABLED=false
|
|
235
|
+
|
|
233
236
|
# Whether or not to enable the CORS headers [false]
|
|
234
237
|
CORS_ENABLED=true
|
|
235
238
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -9,8 +9,10 @@ export declare const COLUMN_TRANSFORMS: string[];
|
|
|
9
9
|
export declare const GENERATE_SPECIAL: string[];
|
|
10
10
|
export declare const UUID_REGEX = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
11
11
|
export declare const REFRESH_COOKIE_OPTIONS: CookieOptions;
|
|
12
|
+
export declare const REFRESH_COOKIE_CLEAR_OPTIONS: CookieOptions;
|
|
12
13
|
export declare const GET_SET_HEADER: (cookieValue: string) => string;
|
|
13
14
|
export declare const ACCESS_COOKIE_OPTIONS: CookieOptions;
|
|
15
|
+
export declare const ACCESS_COOKIE_CLEAR_OPTIONS: CookieOptions;
|
|
14
16
|
export declare const OAS_REQUIRED_SCHEMAS: string[];
|
|
15
17
|
/** Formats from which transformation is supported */
|
|
16
18
|
export declare const SUPPORTED_IMAGE_TRANSFORM_FORMATS: string[];
|
package/dist/constants.js
CHANGED
|
@@ -56,6 +56,8 @@ export const REFRESH_COOKIE_OPTIONS = {
|
|
|
56
56
|
sameSite: env['REFRESH_TOKEN_COOKIE_SAME_SITE'] || 'strict',
|
|
57
57
|
path: '/auth',
|
|
58
58
|
};
|
|
59
|
+
const { maxAge: refreshCookieMaxAge, expires: refreshCookieExpires, ...refresh_cookie_clear_options } = REFRESH_COOKIE_OPTIONS;
|
|
60
|
+
export const REFRESH_COOKIE_CLEAR_OPTIONS = refresh_cookie_clear_options;
|
|
59
61
|
export const GET_SET_HEADER = (cookieValue) => {
|
|
60
62
|
const domainHeader = env['REFRESH_TOKEN_COOKIE_DOMAIN'] ? ` Domain=${env['REFRESH_TOKEN_COOKIE_DOMAIN']};` : '';
|
|
61
63
|
const partitionedHeader = env['REFRESH_TOKEN_COOKIE_PARTITIONED'] == true ? ' Partitioned;' : '';
|
|
@@ -70,6 +72,8 @@ export const ACCESS_COOKIE_OPTIONS = {
|
|
|
70
72
|
sameSite: env['ACCESS_TOKEN_COOKIE_SAME_SITE'] || 'strict',
|
|
71
73
|
path: '/',
|
|
72
74
|
};
|
|
75
|
+
const { maxAge: accessCookieMaxAge, expires: accessCookieExpires, ...access_cookie_clear_options } = ACCESS_COOKIE_OPTIONS;
|
|
76
|
+
export const ACCESS_COOKIE_CLEAR_OPTIONS = access_cookie_clear_options;
|
|
73
77
|
export const OAS_REQUIRED_SCHEMAS = ['Diff', 'Schema', 'Query', 'x-metadata'];
|
|
74
78
|
/** Formats from which transformation is supported */
|
|
75
79
|
export const SUPPORTED_IMAGE_TRANSFORM_FORMATS = ['image/jpeg', 'image/png', 'image/webp', 'image/tiff', 'image/avif'];
|
package/dist/controllers/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { createLDAPAuthRouter, createLocalAuthRouter, createOAuth2AuthRouter, createOpenIDAuthRouter, createSAMLAuthRouter, } from '../auth/drivers/index.js';
|
|
3
|
-
import { ACCESS_COOKIE_OPTIONS, DEFAULT_AUTH_PROVIDER, REFRESH_COOKIE_OPTIONS } from '../constants.js';
|
|
3
|
+
import { ACCESS_COOKIE_CLEAR_OPTIONS, ACCESS_COOKIE_OPTIONS, DEFAULT_AUTH_PROVIDER, REFRESH_COOKIE_CLEAR_OPTIONS, REFRESH_COOKIE_OPTIONS, } from '../constants.js';
|
|
4
4
|
import env from '../env.js';
|
|
5
5
|
import { InvalidPayloadException } from '../exceptions/index.js';
|
|
6
6
|
import logger from '../logger.js';
|
|
@@ -55,6 +55,9 @@ router.post('/refresh', asyncHandler(async (req, res, next) => {
|
|
|
55
55
|
accountability: accountability,
|
|
56
56
|
schema: req.schema,
|
|
57
57
|
});
|
|
58
|
+
if (req.cookies[env['ACCESS_TOKEN_COOKIE_NAME']]) {
|
|
59
|
+
res.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'], ACCESS_COOKIE_CLEAR_OPTIONS);
|
|
60
|
+
}
|
|
58
61
|
const currentRefreshToken = req.body.refresh_token || req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
|
59
62
|
if (!currentRefreshToken) {
|
|
60
63
|
throw new InvalidPayloadException(`"refresh_token" is required in either the JSON payload or Cookie`);
|
|
@@ -95,10 +98,10 @@ router.post('/logout', asyncHandler(async (req, res, next) => {
|
|
|
95
98
|
}
|
|
96
99
|
await authenticationService.logout(currentRefreshToken);
|
|
97
100
|
if (req.cookies[env['ACCESS_TOKEN_COOKIE_NAME']]) {
|
|
98
|
-
res.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'],
|
|
101
|
+
res.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'], ACCESS_COOKIE_CLEAR_OPTIONS);
|
|
99
102
|
}
|
|
100
103
|
if (req.cookies[env['REFRESH_TOKEN_COOKIE_NAME']]) {
|
|
101
|
-
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'],
|
|
104
|
+
res.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'], REFRESH_COOKIE_CLEAR_OPTIONS);
|
|
102
105
|
}
|
|
103
106
|
return next();
|
|
104
107
|
}), respond);
|
package/dist/database/run-ast.js
CHANGED
|
@@ -35,7 +35,7 @@ export default async function runAST(originalAST, schema, options) {
|
|
|
35
35
|
return null;
|
|
36
36
|
// Run the items through the special transforms
|
|
37
37
|
const payloadService = new PayloadService(collection, { knex, schema });
|
|
38
|
-
let items = await payloadService.processValues('read', rawItems);
|
|
38
|
+
let items = await payloadService.processValues('read', rawItems, query.alias ?? {});
|
|
39
39
|
if (!items || (Array.isArray(items) && items.length === 0))
|
|
40
40
|
return items;
|
|
41
41
|
// Apply the `_in` filters to the nested collection batches
|
package/dist/env.js
CHANGED
|
@@ -54,6 +54,7 @@ const allowedEnvironmentVars = [
|
|
|
54
54
|
'HSTS_.+',
|
|
55
55
|
// hashing
|
|
56
56
|
'HASH_.+',
|
|
57
|
+
'X_POWERED_BY_ENABLED',
|
|
57
58
|
// cors
|
|
58
59
|
'CORS_ENABLED',
|
|
59
60
|
'CORS_ORIGIN',
|
|
@@ -226,6 +227,7 @@ const defaults = {
|
|
|
226
227
|
LOGIN_STALL_TIME: 500,
|
|
227
228
|
SERVER_SHUTDOWN_TIMEOUT: 1000,
|
|
228
229
|
ROOT_REDIRECT: './admin',
|
|
230
|
+
X_POWERED_BY_ENABLED: true,
|
|
229
231
|
CORS_ENABLED: false,
|
|
230
232
|
CORS_ORIGIN: false,
|
|
231
233
|
CORS_METHODS: 'GET,POST,PATCH,DELETE',
|
|
@@ -2,7 +2,7 @@ import { isEqual } from 'lodash-es';
|
|
|
2
2
|
import getDatabase from '../database/index.js';
|
|
3
3
|
import emitter from '../emitter.js';
|
|
4
4
|
import env from '../env.js';
|
|
5
|
-
import { InvalidCredentialsException } from '../exceptions/index.js';
|
|
5
|
+
import { InvalidCredentialsException, TokenExpiredException } from '../exceptions/index.js';
|
|
6
6
|
import asyncHandler from '../utils/async-handler.js';
|
|
7
7
|
import { getIPFromReq } from '../utils/get-ip-from-req.js';
|
|
8
8
|
import isDirectusJWT from '../utils/is-directus-jwt.js';
|
|
@@ -40,6 +40,20 @@ export const handler = async (req, _res, next) => {
|
|
|
40
40
|
if (req.token) {
|
|
41
41
|
if (isDirectusJWT(req.token)) {
|
|
42
42
|
const payload = verifyAccessJWT(req.token, env['SECRET']);
|
|
43
|
+
// Try finding the user with a valid refresh token stored in the access token
|
|
44
|
+
const user = await database
|
|
45
|
+
.select({
|
|
46
|
+
session_expires: 's.expires',
|
|
47
|
+
user_id: 'u.id',
|
|
48
|
+
})
|
|
49
|
+
.from('directus_sessions AS s')
|
|
50
|
+
.leftJoin('directus_users AS u', 's.user', 'u.id')
|
|
51
|
+
.where('s.token', payload.refresh_token)
|
|
52
|
+
.andWhere('s.expires', '>=', new Date())
|
|
53
|
+
.first();
|
|
54
|
+
if (!user) {
|
|
55
|
+
throw new TokenExpiredException();
|
|
56
|
+
}
|
|
43
57
|
req.accountability.role = payload.role;
|
|
44
58
|
req.accountability.admin = payload.admin_access === true || payload.admin_access == 1;
|
|
45
59
|
req.accountability.app = payload.app_access === true || payload.app_access == 1;
|
|
@@ -128,11 +128,22 @@ export class AuthenticationService {
|
|
|
128
128
|
throw new InvalidOTPException(`"otp" is invalid`);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
+
const refreshToken = nanoid(64);
|
|
132
|
+
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
133
|
+
await this.knex('directus_sessions').insert({
|
|
134
|
+
token: refreshToken,
|
|
135
|
+
user: user.id,
|
|
136
|
+
expires: refreshTokenExpiration,
|
|
137
|
+
ip: this.accountability?.ip,
|
|
138
|
+
user_agent: this.accountability?.userAgent,
|
|
139
|
+
origin: this.accountability?.origin,
|
|
140
|
+
});
|
|
131
141
|
const tokenPayload = {
|
|
132
142
|
id: user.id,
|
|
133
143
|
role: user.role,
|
|
134
144
|
app_access: user.app_access,
|
|
135
145
|
admin_access: user.admin_access,
|
|
146
|
+
refresh_token: refreshToken,
|
|
136
147
|
};
|
|
137
148
|
const customClaims = await emitter.emitFilter('auth.jwt', tokenPayload, {
|
|
138
149
|
status: 'pending',
|
|
@@ -148,16 +159,6 @@ export class AuthenticationService {
|
|
|
148
159
|
expiresIn: env['ACCESS_TOKEN_TTL'],
|
|
149
160
|
issuer: 'directus',
|
|
150
161
|
});
|
|
151
|
-
const refreshToken = nanoid(64);
|
|
152
|
-
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
153
|
-
await this.knex('directus_sessions').insert({
|
|
154
|
-
token: refreshToken,
|
|
155
|
-
user: user.id,
|
|
156
|
-
expires: refreshTokenExpiration,
|
|
157
|
-
ip: this.accountability?.ip,
|
|
158
|
-
user_agent: this.accountability?.userAgent,
|
|
159
|
-
origin: this.accountability?.origin,
|
|
160
|
-
});
|
|
161
162
|
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
|
162
163
|
if (this.accountability) {
|
|
163
164
|
await this.activityService.createOne({
|
|
@@ -260,11 +261,20 @@ export class AuthenticationService {
|
|
|
260
261
|
admin_access: record.role_admin_access,
|
|
261
262
|
});
|
|
262
263
|
}
|
|
264
|
+
const newRefreshToken = nanoid(64);
|
|
265
|
+
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
266
|
+
await this.knex('directus_sessions')
|
|
267
|
+
.update({
|
|
268
|
+
token: newRefreshToken,
|
|
269
|
+
expires: refreshTokenExpiration,
|
|
270
|
+
})
|
|
271
|
+
.where({ token: refreshToken });
|
|
263
272
|
const tokenPayload = {
|
|
264
273
|
id: record.user_id,
|
|
265
274
|
role: record.role_id,
|
|
266
275
|
app_access: record.role_app_access,
|
|
267
276
|
admin_access: record.role_admin_access,
|
|
277
|
+
refresh_token: newRefreshToken,
|
|
268
278
|
};
|
|
269
279
|
if (record.share_id) {
|
|
270
280
|
tokenPayload.share = record.share_id;
|
|
@@ -291,14 +301,6 @@ export class AuthenticationService {
|
|
|
291
301
|
expiresIn: env['ACCESS_TOKEN_TTL'],
|
|
292
302
|
issuer: 'directus',
|
|
293
303
|
});
|
|
294
|
-
const newRefreshToken = nanoid(64);
|
|
295
|
-
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
296
|
-
await this.knex('directus_sessions')
|
|
297
|
-
.update({
|
|
298
|
-
token: newRefreshToken,
|
|
299
|
-
expires: refreshTokenExpiration,
|
|
300
|
-
})
|
|
301
|
-
.where({ token: refreshToken });
|
|
302
304
|
if (record.user_id) {
|
|
303
305
|
await this.knex('directus_users').update({ last_access: new Date() }).where({ id: record.user_id });
|
|
304
306
|
}
|
|
@@ -5,7 +5,7 @@ import { GraphQLBoolean, GraphQLEnumType, GraphQLError, GraphQLFloat, GraphQLID,
|
|
|
5
5
|
import { GraphQLJSON, InputTypeComposer, ObjectTypeComposer, SchemaComposer, toInputObjectType } from 'graphql-compose';
|
|
6
6
|
import { flatten, get, mapKeys, merge, omit, pick, set, transform, uniq } from 'lodash-es';
|
|
7
7
|
import { clearSystemCache, getCache } from '../../cache.js';
|
|
8
|
-
import { ACCESS_COOKIE_OPTIONS, DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL, REFRESH_COOKIE_OPTIONS, } from '../../constants.js';
|
|
8
|
+
import { ACCESS_COOKIE_CLEAR_OPTIONS, ACCESS_COOKIE_OPTIONS, DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL, REFRESH_COOKIE_CLEAR_OPTIONS, REFRESH_COOKIE_OPTIONS, } from '../../constants.js';
|
|
9
9
|
import getDatabase from '../../database/index.js';
|
|
10
10
|
import env from '../../env.js';
|
|
11
11
|
import { ForbiddenException, GraphQLValidationException, InvalidPayloadException } from '../../exceptions/index.js';
|
|
@@ -1766,10 +1766,10 @@ export class GraphQLService {
|
|
|
1766
1766
|
}
|
|
1767
1767
|
await authenticationService.logout(currentRefreshToken);
|
|
1768
1768
|
if (req?.cookies?.[env['ACCESS_TOKEN_COOKIE_NAME']]) {
|
|
1769
|
-
res?.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'],
|
|
1769
|
+
res?.clearCookie(env['ACCESS_TOKEN_COOKIE_NAME'], ACCESS_COOKIE_CLEAR_OPTIONS);
|
|
1770
1770
|
}
|
|
1771
1771
|
if (req?.cookies?.[env['REFRESH_TOKEN_COOKIE_NAME']]) {
|
|
1772
|
-
res?.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'],
|
|
1772
|
+
res?.clearCookie(env['REFRESH_TOKEN_COOKIE_NAME'], REFRESH_COOKIE_CLEAR_OPTIONS);
|
|
1773
1773
|
}
|
|
1774
1774
|
return true;
|
|
1775
1775
|
},
|
|
@@ -27,6 +27,8 @@ export declare class PayloadService {
|
|
|
27
27
|
transformers: Transformers;
|
|
28
28
|
processValues(action: Action, payloads: Partial<Item>[]): Promise<Partial<Item>[]>;
|
|
29
29
|
processValues(action: Action, payload: Partial<Item>): Promise<Partial<Item>>;
|
|
30
|
+
processValues(action: Action, payloads: Partial<Item>[], aliasMap: Record<string, string>): Promise<Partial<Item>[]>;
|
|
31
|
+
processValues(action: Action, payload: Partial<Item>, aliasMap: Record<string, string>): Promise<Partial<Item>>;
|
|
30
32
|
processAggregates(payload: Partial<Item>[]): void;
|
|
31
33
|
processField(field: SchemaOverview['collections'][string]['fields'][string], payload: Partial<Item>, action: Action, accountability: Accountability | null): Promise<any>;
|
|
32
34
|
/**
|
package/dist/services/payload.js
CHANGED
|
@@ -121,12 +121,20 @@ export class PayloadService {
|
|
|
121
121
|
return value;
|
|
122
122
|
},
|
|
123
123
|
};
|
|
124
|
-
async processValues(action, payload) {
|
|
124
|
+
async processValues(action, payload, aliasMap = {}) {
|
|
125
125
|
const processedPayload = toArray(payload);
|
|
126
126
|
if (processedPayload.length === 0)
|
|
127
127
|
return [];
|
|
128
128
|
const fieldsInPayload = Object.keys(processedPayload[0]);
|
|
129
129
|
let specialFieldsInCollection = Object.entries(this.schema.collections[this.collection].fields).filter(([_name, field]) => field.special && field.special.length > 0);
|
|
130
|
+
const aliasEntries = Object.entries(aliasMap);
|
|
131
|
+
for (const [name, field] of specialFieldsInCollection) {
|
|
132
|
+
for (const [aliasName, fieldName] of aliasEntries) {
|
|
133
|
+
if (fieldName === name) {
|
|
134
|
+
specialFieldsInCollection.push([aliasName, { ...field, field: aliasName }]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
130
138
|
if (action === 'read') {
|
|
131
139
|
specialFieldsInCollection = specialFieldsInCollection.filter(([name]) => {
|
|
132
140
|
return fieldsInPayload.includes(name);
|
package/dist/services/shares.js
CHANGED
|
@@ -59,6 +59,17 @@ export class SharesService extends ItemsService {
|
|
|
59
59
|
await this.knex('directus_shares')
|
|
60
60
|
.update({ times_used: record.share_times_used + 1 })
|
|
61
61
|
.where('id', record.share_id);
|
|
62
|
+
const refreshToken = nanoid(64);
|
|
63
|
+
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
64
|
+
await this.knex('directus_sessions').insert({
|
|
65
|
+
token: refreshToken,
|
|
66
|
+
expires: refreshTokenExpiration,
|
|
67
|
+
ip: this.accountability?.ip,
|
|
68
|
+
user_agent: this.accountability?.userAgent,
|
|
69
|
+
origin: this.accountability?.origin,
|
|
70
|
+
share: record.share_id,
|
|
71
|
+
});
|
|
72
|
+
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
|
62
73
|
const tokenPayload = {
|
|
63
74
|
app_access: false,
|
|
64
75
|
admin_access: false,
|
|
@@ -68,22 +79,12 @@ export class SharesService extends ItemsService {
|
|
|
68
79
|
item: record.share_item,
|
|
69
80
|
collection: record.share_collection,
|
|
70
81
|
},
|
|
82
|
+
refresh_token: refreshToken,
|
|
71
83
|
};
|
|
72
84
|
const accessToken = jwt.sign(tokenPayload, env['SECRET'], {
|
|
73
85
|
expiresIn: env['ACCESS_TOKEN_TTL'],
|
|
74
86
|
issuer: 'directus',
|
|
75
87
|
});
|
|
76
|
-
const refreshToken = nanoid(64);
|
|
77
|
-
const refreshTokenExpiration = new Date(Date.now() + getMilliseconds(env['REFRESH_TOKEN_TTL'], 0));
|
|
78
|
-
await this.knex('directus_sessions').insert({
|
|
79
|
-
token: refreshToken,
|
|
80
|
-
expires: refreshTokenExpiration,
|
|
81
|
-
ip: this.accountability?.ip,
|
|
82
|
-
user_agent: this.accountability?.userAgent,
|
|
83
|
-
origin: this.accountability?.origin,
|
|
84
|
-
share: record.share_id,
|
|
85
|
-
});
|
|
86
|
-
await this.knex('directus_sessions').delete().where('expires', '<', new Date());
|
|
87
88
|
return {
|
|
88
89
|
accessToken,
|
|
89
90
|
refreshToken,
|
package/dist/types/auth.d.ts
CHANGED
package/dist/utils/jwt.js
CHANGED
|
@@ -21,9 +21,9 @@ export function verifyJWT(token, secret) {
|
|
|
21
21
|
return payload;
|
|
22
22
|
}
|
|
23
23
|
export function verifyAccessJWT(token, secret) {
|
|
24
|
-
const { id, role, app_access, admin_access, share, share_scope } = verifyJWT(token, secret);
|
|
25
|
-
if (role === undefined || app_access === undefined || admin_access === undefined) {
|
|
24
|
+
const { id, role, app_access, admin_access, share, share_scope, refresh_token } = verifyJWT(token, secret);
|
|
25
|
+
if (role === undefined || app_access === undefined || admin_access === undefined || refresh_token === undefined) {
|
|
26
26
|
throw new InvalidTokenException('Invalid token payload.');
|
|
27
27
|
}
|
|
28
|
-
return { id, role, app_access, admin_access, share, share_scope };
|
|
28
|
+
return { id, role, app_access, admin_access, share, share_scope, refresh_token };
|
|
29
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wbce-d9/api",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -143,20 +143,20 @@
|
|
|
143
143
|
"uuid-validate": "0.0.3",
|
|
144
144
|
"vm2": "^3.9.19",
|
|
145
145
|
"wellknown": "0.5.0",
|
|
146
|
-
"@wbce-d9/app": "9.
|
|
147
|
-
"@wbce-d9/constants": "9.26.4",
|
|
148
|
-
"@wbce-d9/extensions-sdk": "9.26.6",
|
|
149
|
-
"@wbce-d9/specs": "9.26.4",
|
|
146
|
+
"@wbce-d9/app": "9.30.0",
|
|
150
147
|
"@wbce-d9/exceptions": "9.26.4",
|
|
151
|
-
"@wbce-d9/
|
|
148
|
+
"@wbce-d9/constants": "9.27.0",
|
|
149
|
+
"@wbce-d9/extensions-sdk": "9.26.7",
|
|
152
150
|
"@wbce-d9/schema": "9.26.4",
|
|
153
|
-
"@wbce-d9/storage
|
|
154
|
-
"@wbce-d9/
|
|
155
|
-
"@wbce-d9/storage-driver-
|
|
156
|
-
"@wbce-d9/storage-driver-
|
|
157
|
-
"@wbce-d9/storage-driver-
|
|
151
|
+
"@wbce-d9/storage": "9.26.4",
|
|
152
|
+
"@wbce-d9/specs": "9.26.4",
|
|
153
|
+
"@wbce-d9/storage-driver-azure": "9.26.7",
|
|
154
|
+
"@wbce-d9/storage-driver-cloudinary": "9.26.7",
|
|
155
|
+
"@wbce-d9/storage-driver-gcs": "9.26.7",
|
|
158
156
|
"@wbce-d9/update-check": "9.26.4",
|
|
159
|
-
"@wbce-d9/
|
|
157
|
+
"@wbce-d9/storage-driver-local": "9.26.7",
|
|
158
|
+
"@wbce-d9/storage-driver-s3": "9.26.9",
|
|
159
|
+
"@wbce-d9/utils": "9.29.0"
|
|
160
160
|
},
|
|
161
161
|
"devDependencies": {
|
|
162
162
|
"@ngneat/falso": "6.4.0",
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"supertest": "6.3.3",
|
|
205
205
|
"typescript": "5.0.4",
|
|
206
206
|
"vitest": "0.30.1",
|
|
207
|
-
"@wbce-d9/types": "9.
|
|
207
|
+
"@wbce-d9/types": "9.27.0"
|
|
208
208
|
},
|
|
209
209
|
"optionalDependencies": {
|
|
210
210
|
"@keyv/redis": "2.5.7",
|