@directus/api 32.1.0 → 32.1.1

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.
@@ -5,11 +5,12 @@ import type { RoleMap } from '../../types/rolemap.js';
5
5
  import { LocalAuthDriver } from './local.js';
6
6
  export declare class OAuth2AuthDriver extends LocalAuthDriver {
7
7
  client: Client;
8
+ redirectUrl: string;
8
9
  config: Record<string, any>;
9
10
  roleMap: RoleMap;
10
11
  constructor(options: AuthDriverOptions, config: Record<string, any>);
11
12
  generateCodeVerifier(): string;
12
- generateAuthUrl(codeVerifier: string, prompt?: boolean, callbackUrl?: string): string;
13
+ generateAuthUrl(codeVerifier: string, prompt?: boolean): string;
13
14
  private fetchUserId;
14
15
  getUserID(payload: Record<string, any>): Promise<string>;
15
16
  login(user: User): Promise<void>;
@@ -16,37 +16,40 @@ import { AuthenticationService } from '../../services/authentication.js';
16
16
  import asyncHandler from '../../utils/async-handler.js';
17
17
  import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
18
18
  import { getIPFromReq } from '../../utils/get-ip-from-req.js';
19
- import { getSchema } from '../../utils/get-schema.js';
20
19
  import { getSecret } from '../../utils/get-secret.js';
20
+ import { isLoginRedirectAllowed } from '../../utils/is-login-redirect-allowed.js';
21
21
  import { verifyJWT } from '../../utils/jwt.js';
22
22
  import { Url } from '../../utils/url.js';
23
- import { generateCallbackUrl } from '../utils/generate-callback-url.js';
24
- import { isLoginRedirectAllowed } from '../utils/is-login-redirect-allowed.js';
25
23
  import { LocalAuthDriver } from './local.js';
24
+ import { getSchema } from '../../utils/get-schema.js';
26
25
  export class OAuth2AuthDriver extends LocalAuthDriver {
27
26
  client;
27
+ redirectUrl;
28
28
  config;
29
29
  roleMap;
30
30
  constructor(options, config) {
31
31
  super(options, config);
32
+ const env = useEnv();
32
33
  const logger = useLogger();
33
34
  const { authorizeUrl, accessUrl, profileUrl, clientId, clientSecret, ...additionalConfig } = config;
34
35
  if (!authorizeUrl || !accessUrl || !profileUrl || !clientId || !clientSecret || !additionalConfig['provider']) {
35
36
  logger.error('Invalid provider config');
36
37
  throw new InvalidProviderConfigError({ provider: additionalConfig['provider'] });
37
38
  }
39
+ const redirectUrl = new Url(env['PUBLIC_URL']).addPath('auth', 'login', additionalConfig['provider'], 'callback');
40
+ this.redirectUrl = redirectUrl.toString();
38
41
  this.config = additionalConfig;
39
42
  this.roleMap = {};
40
43
  const roleMapping = this.config['roleMapping'];
44
+ if (roleMapping) {
45
+ this.roleMap = roleMapping;
46
+ }
41
47
  // role mapping will fail on login if AUTH_<provider>_ROLE_MAPPING is an array instead of an object.
42
48
  // This happens if the 'json:' prefix is missing from the variable declaration. To save the user from exhaustive debugging, we'll try to fail early here.
43
49
  if (roleMapping instanceof Array) {
44
50
  logger.error("[OAuth2] Expected a JSON-Object as role mapping, got an Array instead. Make sure you declare the variable with 'json:' prefix.");
45
51
  throw new InvalidProviderError();
46
52
  }
47
- if (roleMapping) {
48
- this.roleMap = roleMapping;
49
- }
50
53
  const issuer = new Issuer({
51
54
  authorization_endpoint: authorizeUrl,
52
55
  token_endpoint: accessUrl,
@@ -64,6 +67,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
64
67
  this.client = new issuer.Client({
65
68
  client_id: clientId,
66
69
  client_secret: clientSecret,
70
+ redirect_uris: [this.redirectUrl],
67
71
  response_types: ['code'],
68
72
  ...clientOptionsOverrides,
69
73
  });
@@ -71,7 +75,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
71
75
  generateCodeVerifier() {
72
76
  return generators.codeVerifier();
73
77
  }
74
- generateAuthUrl(codeVerifier, prompt = false, callbackUrl) {
78
+ generateAuthUrl(codeVerifier, prompt = false) {
75
79
  const { plainCodeChallenge } = this.config;
76
80
  try {
77
81
  const codeChallenge = plainCodeChallenge ? codeVerifier : generators.codeChallenge(codeVerifier);
@@ -85,7 +89,6 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
85
89
  code_challenge_method: plainCodeChallenge ? 'plain' : 'S256',
86
90
  // Some providers require state even with PKCE
87
91
  state: codeChallenge,
88
- redirect_uri: callbackUrl,
89
92
  });
90
93
  }
91
94
  catch (e) {
@@ -113,7 +116,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
113
116
  const codeChallenge = plainCodeChallenge
114
117
  ? payload['codeVerifier']
115
118
  : generators.codeChallenge(payload['codeVerifier']);
116
- tokenSet = await this.client.oauthCallback(payload['redirectUri'], { code: payload['code'], state: payload['state'] }, { code_verifier: payload['codeVerifier'], state: codeChallenge });
119
+ tokenSet = await this.client.oauthCallback(this.redirectUrl, { code: payload['code'], state: payload['state'] }, { code_verifier: payload['codeVerifier'], state: codeChallenge });
117
120
  userInfo = await this.client.userinfo(tokenSet.access_token);
118
121
  }
119
122
  catch (e) {
@@ -272,19 +275,12 @@ export function createOAuth2AuthRouter(providerName) {
272
275
  const provider = getAuthProvider(providerName);
273
276
  const codeVerifier = provider.generateCodeVerifier();
274
277
  const prompt = !!req.query['prompt'];
275
- const otp = req.query['otp'];
276
278
  const redirect = req.query['redirect'];
277
- if (!isLoginRedirectAllowed(providerName, `${req.protocol}://${req.hostname}`, redirect)) {
279
+ const otp = req.query['otp'];
280
+ if (isLoginRedirectAllowed(redirect, providerName) === false) {
278
281
  throw new InvalidPayloadError({ reason: `URL "${redirect}" can't be used to redirect after login` });
279
282
  }
280
- const callbackUrl = generateCallbackUrl(providerName, `${req.protocol}://${req.get('host')}`);
281
- const token = jwt.sign({
282
- verifier: codeVerifier,
283
- redirect,
284
- prompt,
285
- otp,
286
- callbackUrl,
287
- }, getSecret(), {
283
+ const token = jwt.sign({ verifier: codeVerifier, redirect, prompt, otp }, getSecret(), {
288
284
  expiresIn: '5m',
289
285
  issuer: 'directus',
290
286
  });
@@ -292,7 +288,7 @@ export function createOAuth2AuthRouter(providerName) {
292
288
  httpOnly: true,
293
289
  sameSite: 'lax',
294
290
  });
295
- return res.redirect(provider.generateAuthUrl(codeVerifier, prompt, callbackUrl));
291
+ return res.redirect(provider.generateAuthUrl(codeVerifier, prompt));
296
292
  }, respond);
297
293
  router.post('/callback', express.urlencoded({ extended: false }), (req, res) => {
298
294
  res.redirect(303, `./callback?${new URLSearchParams(req.body)}`);
@@ -307,7 +303,7 @@ export function createOAuth2AuthRouter(providerName) {
307
303
  logger.warn(e, `[OAuth2] Couldn't verify OAuth2 cookie`);
308
304
  throw new InvalidCredentialsError();
309
305
  }
310
- const { verifier, prompt, otp, callbackUrl } = tokenData;
306
+ const { verifier, prompt, otp } = tokenData;
311
307
  let { redirect } = tokenData;
312
308
  const accountability = createDefaultAccountability({
313
309
  ip: getIPFromReq(req),
@@ -330,7 +326,6 @@ export function createOAuth2AuthRouter(providerName) {
330
326
  code: req.query['code'],
331
327
  codeVerifier: verifier,
332
328
  state: req.query['state'],
333
- callbackUrl,
334
329
  }, { session: authMode === 'session', ...(otp ? { otp: String(otp) } : {}) });
335
330
  }
336
331
  catch (error) {
@@ -5,12 +5,13 @@ import type { RoleMap } from '../../types/rolemap.js';
5
5
  import { LocalAuthDriver } from './local.js';
6
6
  export declare class OpenIDAuthDriver extends LocalAuthDriver {
7
7
  client: null | Client;
8
+ redirectUrl: string;
8
9
  config: Record<string, any>;
9
10
  roleMap: RoleMap;
10
11
  constructor(options: AuthDriverOptions, config: Record<string, any>);
11
12
  private getClient;
12
13
  generateCodeVerifier(): string;
13
- generateAuthUrl(codeVerifier: string, prompt?: boolean, callbackUrl?: string): Promise<string>;
14
+ generateAuthUrl(codeVerifier: string, prompt?: boolean): Promise<string>;
14
15
  private fetchUserId;
15
16
  getUserID(payload: Record<string, any>): Promise<string>;
16
17
  login(user: User): Promise<void>;
@@ -16,19 +16,20 @@ import { AuthenticationService } from '../../services/authentication.js';
16
16
  import asyncHandler from '../../utils/async-handler.js';
17
17
  import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
18
18
  import { getIPFromReq } from '../../utils/get-ip-from-req.js';
19
- import { getSchema } from '../../utils/get-schema.js';
20
19
  import { getSecret } from '../../utils/get-secret.js';
20
+ import { isLoginRedirectAllowed } from '../../utils/is-login-redirect-allowed.js';
21
21
  import { verifyJWT } from '../../utils/jwt.js';
22
22
  import { Url } from '../../utils/url.js';
23
- import { generateCallbackUrl } from '../utils/generate-callback-url.js';
24
- import { isLoginRedirectAllowed } from '../utils/is-login-redirect-allowed.js';
25
23
  import { LocalAuthDriver } from './local.js';
24
+ import { getSchema } from '../../utils/get-schema.js';
26
25
  export class OpenIDAuthDriver extends LocalAuthDriver {
27
26
  client;
27
+ redirectUrl;
28
28
  config;
29
29
  roleMap;
30
30
  constructor(options, config) {
31
31
  super(options, config);
32
+ const env = useEnv();
32
33
  const logger = useLogger();
33
34
  const { issuerUrl, clientId, clientSecret, clientPrivateKeys, clientTokenEndpointAuthMethod, provider, issuerDiscoveryMustSucceed, } = config;
34
35
  const isPrivateKeyJwtAuthMethod = clientTokenEndpointAuthMethod === 'private_key_jwt';
@@ -36,6 +37,8 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
36
37
  logger.error('Invalid provider config');
37
38
  throw new InvalidProviderConfigError({ provider });
38
39
  }
40
+ const redirectUrl = new Url(env['PUBLIC_URL']).addPath('auth', 'login', provider, 'callback');
41
+ this.redirectUrl = redirectUrl.toString();
39
42
  this.config = config;
40
43
  this.roleMap = {};
41
44
  const roleMapping = this.config['roleMapping'];
@@ -95,6 +98,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
95
98
  const client = new issuer.Client({
96
99
  client_id: clientId,
97
100
  ...(!isPrivateKeyJwtAuthMethod && { client_secret: clientSecret }),
101
+ redirect_uris: [this.redirectUrl],
98
102
  response_types: ['code'],
99
103
  ...clientOptionsOverrides,
100
104
  }, isPrivateKeyJwtAuthMethod ? { keys: clientPrivateKeys } : undefined);
@@ -112,7 +116,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
112
116
  generateCodeVerifier() {
113
117
  return generators.codeVerifier();
114
118
  }
115
- async generateAuthUrl(codeVerifier, prompt = false, callbackUrl) {
119
+ async generateAuthUrl(codeVerifier, prompt = false) {
116
120
  const { plainCodeChallenge } = this.config;
117
121
  try {
118
122
  const client = await this.getClient();
@@ -128,7 +132,6 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
128
132
  // Some providers require state even with PKCE
129
133
  state: codeChallenge,
130
134
  nonce: codeChallenge,
131
- redirect_uri: callbackUrl,
132
135
  });
133
136
  }
134
137
  catch (e) {
@@ -157,7 +160,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
157
160
  const codeChallenge = plainCodeChallenge
158
161
  ? payload['codeVerifier']
159
162
  : generators.codeChallenge(payload['codeVerifier']);
160
- tokenSet = await client.callback(payload['callbackUrl'], { code: payload['code'], state: payload['state'], iss: payload['iss'] }, { code_verifier: payload['codeVerifier'], state: codeChallenge, nonce: codeChallenge });
163
+ tokenSet = await client.callback(this.redirectUrl, { code: payload['code'], state: payload['state'], iss: payload['iss'] }, { code_verifier: payload['codeVerifier'], state: codeChallenge, nonce: codeChallenge });
161
164
  userInfo = tokenSet.claims();
162
165
  if (client.issuer.metadata['userinfo_endpoint']) {
163
166
  userInfo = {
@@ -326,17 +329,10 @@ export function createOpenIDAuthRouter(providerName) {
326
329
  const prompt = !!req.query['prompt'];
327
330
  const redirect = req.query['redirect'];
328
331
  const otp = req.query['otp'];
329
- if (!isLoginRedirectAllowed(providerName, `${req.protocol}://${req.hostname}`, redirect)) {
332
+ if (isLoginRedirectAllowed(redirect, providerName) === false) {
330
333
  throw new InvalidPayloadError({ reason: `URL "${redirect}" can't be used to redirect after login` });
331
334
  }
332
- const callbackUrl = generateCallbackUrl(providerName, `${req.protocol}://${req.get('host')}`);
333
- const token = jwt.sign({
334
- verifier: codeVerifier,
335
- redirect,
336
- prompt,
337
- otp,
338
- callbackUrl,
339
- }, getSecret(), {
335
+ const token = jwt.sign({ verifier: codeVerifier, redirect, prompt, otp }, getSecret(), {
340
336
  expiresIn: (env[`AUTH_${providerName.toUpperCase()}_LOGIN_TIMEOUT`] ?? '5m'),
341
337
  issuer: 'directus',
342
338
  });
@@ -345,7 +341,7 @@ export function createOpenIDAuthRouter(providerName) {
345
341
  sameSite: 'lax',
346
342
  });
347
343
  try {
348
- return res.redirect(await provider.generateAuthUrl(codeVerifier, prompt, callbackUrl));
344
+ return res.redirect(await provider.generateAuthUrl(codeVerifier, prompt));
349
345
  }
350
346
  catch {
351
347
  return res.redirect(new Url(env['PUBLIC_URL'])
@@ -369,7 +365,7 @@ export function createOpenIDAuthRouter(providerName) {
369
365
  const url = new Url(env['PUBLIC_URL']).addPath('admin', 'login');
370
366
  return res.redirect(`${url.toString()}?reason=${ErrorCode.InvalidCredentials}`);
371
367
  }
372
- const { verifier, prompt, otp, callbackUrl } = tokenData;
368
+ const { verifier, prompt, otp } = tokenData;
373
369
  let { redirect } = tokenData;
374
370
  const accountability = createDefaultAccountability({ ip: getIPFromReq(req) });
375
371
  const userAgent = req.get('user-agent')?.substring(0, 1024);
@@ -391,7 +387,6 @@ export function createOpenIDAuthRouter(providerName) {
391
387
  codeVerifier: verifier,
392
388
  state: req.query['state'],
393
389
  iss: req.query['iss'],
394
- callbackUrl,
395
390
  }, { session: authMode === 'session', ...(otp ? { otp: String(otp) } : {}) });
396
391
  }
397
392
  catch (error) {
@@ -13,8 +13,8 @@ import { AuthenticationService } from '../../services/authentication.js';
13
13
  import asyncHandler from '../../utils/async-handler.js';
14
14
  import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
15
15
  import { LocalAuthDriver } from './local.js';
16
+ import { isLoginRedirectAllowed } from '../../utils/is-login-redirect-allowed.js';
16
17
  import { getSchema } from '../../utils/get-schema.js';
17
- import { isLoginRedirectAllowed } from '../utils/is-login-redirect-allowed.js';
18
18
  // Register the samlify schema validator
19
19
  samlify.setSchemaValidator(validator);
20
20
  export class SAMLAuthDriver extends LocalAuthDriver {
@@ -95,7 +95,7 @@ export function createSAMLAuthRouter(providerName) {
95
95
  const parsedUrl = new URL(url);
96
96
  if (req.query['redirect']) {
97
97
  const redirect = req.query['redirect'];
98
- if (!isLoginRedirectAllowed(providerName, `${req.protocol}://${req.hostname}`, redirect)) {
98
+ if (isLoginRedirectAllowed(redirect, providerName) === false) {
99
99
  throw new InvalidPayloadError({ reason: `URL "${redirect}" can't be used to redirect after login` });
100
100
  }
101
101
  parsedUrl.searchParams.append('RelayState', redirect);
@@ -23,10 +23,10 @@ export async function resolveQuery(gql, info) {
23
23
  query = await getAggregateQuery(args, selections, gql.schema, gql.accountability, collection);
24
24
  }
25
25
  else {
26
- query = await getQuery(args, gql.schema, selections, info.variableValues, gql.accountability, collection);
27
26
  if (collection.endsWith('_by_id') && collection in gql.schema.collections === false) {
28
27
  collection = collection.slice(0, -6);
29
28
  }
29
+ query = await getQuery(args, gql.schema, selections, info.variableValues, gql.accountability, collection);
30
30
  if (collection.endsWith('_by_version') && collection in gql.schema.collections === false) {
31
31
  collection = collection.slice(0, -11);
32
32
  query.versionRaw = true;
@@ -1,14 +1,18 @@
1
- import { toBoolean } from '@directus/utils';
1
+ import { SettingsService } from '../../services/settings.js';
2
+ import { getSchema } from '../../utils/get-schema.js';
2
3
  export const getSettings = async (db) => {
3
- const settings = await db
4
- .select('project_id', 'mcp_enabled', 'mcp_allow_deletes', 'mcp_system_prompt_enabled', 'visual_editor_urls')
5
- .from('directus_settings')
6
- .first();
4
+ const settingsService = new SettingsService({
5
+ knex: db,
6
+ schema: await getSchema({ database: db }),
7
+ });
8
+ const settings = (await settingsService.readSingleton({
9
+ fields: ['project_id', 'mcp_enabled', 'mcp_allow_deletes', 'mcp_system_prompt_enabled', 'visual_editor_urls'],
10
+ }));
7
11
  return {
8
12
  project_id: settings.project_id,
9
- mcp_enabled: toBoolean(settings?.mcp_enabled),
10
- mcp_allow_deletes: toBoolean(settings?.mcp_allow_deletes),
11
- mcp_system_prompt_enabled: toBoolean(settings?.mcp_system_prompt_enabled),
12
- visual_editor_urls: settings.visual_editor_urls ? JSON.parse(settings.visual_editor_urls).length : 0,
13
+ mcp_enabled: settings?.mcp_enabled || false,
14
+ mcp_allow_deletes: settings?.mcp_allow_deletes || false,
15
+ mcp_system_prompt_enabled: settings?.mcp_system_prompt_enabled || false,
16
+ visual_editor_urls: settings.visual_editor_urls?.length || 0,
13
17
  };
14
18
  };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Checks if the defined redirect after successful SSO login is in the allow list
3
+ */
4
+ export declare function isLoginRedirectAllowed(redirect: unknown, provider: string): boolean;
@@ -1,23 +1,19 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { toArray } from '@directus/utils';
3
- import { useLogger } from '../../logger/index.js';
4
- import isUrlAllowed from '../../utils/is-url-allowed.js';
3
+ import { useLogger } from '../logger/index.js';
4
+ import isUrlAllowed from './is-url-allowed.js';
5
5
  /**
6
- * Check if the redirect URL is allowed
7
- * @param originUrl Origin URL
8
- * @param provider OAuth provider name
9
- * @param redirect URL to redirect to
10
- * @returns True if the redirect is allowed, false otherwise
6
+ * Checks if the defined redirect after successful SSO login is in the allow list
11
7
  */
12
- export function isLoginRedirectAllowed(provider, originUrl, redirect) {
8
+ export function isLoginRedirectAllowed(redirect, provider) {
13
9
  if (!redirect)
14
10
  return true; // empty redirect
15
11
  if (typeof redirect !== 'string')
16
12
  return false; // invalid type
17
13
  const env = useEnv();
18
14
  const publicUrl = env['PUBLIC_URL'];
19
- if (!URL.canParse(redirect)) {
20
- if (!redirect.startsWith('//')) {
15
+ if (URL.canParse(redirect) === false) {
16
+ if (redirect.startsWith('//') === false) {
21
17
  // should be a relative path like `/admin/test`
22
18
  return true;
23
19
  }
@@ -25,10 +21,6 @@ export function isLoginRedirectAllowed(provider, originUrl, redirect) {
25
21
  return false;
26
22
  }
27
23
  const { protocol: redirectProtocol, hostname: redirectDomain } = new URL(redirect);
28
- const redirectUrl = `${redirectProtocol}//${redirectDomain}`;
29
- // Security check: redirect URL must match the request origin
30
- if (redirectUrl !== originUrl)
31
- return false;
32
24
  const envKey = `AUTH_${provider.toUpperCase()}_REDIRECT_ALLOW_LIST`;
33
25
  if (envKey in env) {
34
26
  if (isUrlAllowed(redirect, [...toArray(env[envKey]), publicUrl]))
@@ -38,7 +30,7 @@ export function isLoginRedirectAllowed(provider, originUrl, redirect) {
38
30
  useLogger().error('Invalid PUBLIC_URL for login redirect');
39
31
  return false;
40
32
  }
41
- const { protocol: publicProtocol, hostname: publicDomain } = new URL(publicUrl);
42
33
  // allow redirects to the defined PUBLIC_URL
43
- return redirectUrl === `${publicProtocol}//${publicDomain}`;
34
+ const { protocol: publicProtocol, hostname: publicDomain } = new URL(publicUrl);
35
+ return `${redirectProtocol}//${redirectDomain}` === `${publicProtocol}//${publicDomain}`;
44
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "32.1.0",
3
+ "version": "32.1.1",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -153,30 +153,30 @@
153
153
  "ws": "8.18.3",
154
154
  "zod": "4.1.12",
155
155
  "zod-validation-error": "4.0.2",
156
- "@directus/app": "14.2.0",
156
+ "@directus/app": "14.3.0",
157
157
  "@directus/env": "5.3.2",
158
- "@directus/constants": "14.0.0",
159
- "@directus/errors": "2.0.5",
160
158
  "@directus/extensions-registry": "3.0.14",
159
+ "@directus/extensions": "3.0.14",
160
+ "@directus/errors": "2.0.5",
161
+ "@directus/constants": "14.0.0",
161
162
  "@directus/extensions-sdk": "17.0.3",
163
+ "@directus/memory": "3.0.12",
162
164
  "@directus/format-title": "12.1.1",
163
- "@directus/extensions": "3.0.14",
164
165
  "@directus/pressure": "3.0.12",
165
- "@directus/schema": "13.0.4",
166
- "@directus/memory": "3.0.12",
167
166
  "@directus/schema-builder": "0.0.9",
168
- "@directus/storage": "12.0.3",
169
167
  "@directus/specs": "11.2.0",
170
- "@directus/storage-driver-azure": "12.0.12",
168
+ "@directus/schema": "13.0.4",
169
+ "@directus/storage": "12.0.3",
171
170
  "@directus/storage-driver-cloudinary": "12.0.12",
172
- "@directus/storage-driver-gcs": "12.0.12",
173
- "@directus/storage-driver-s3": "12.0.12",
171
+ "@directus/storage-driver-azure": "12.0.12",
174
172
  "@directus/storage-driver-local": "12.0.3",
173
+ "@directus/storage-driver-gcs": "12.0.12",
175
174
  "@directus/storage-driver-supabase": "3.0.12",
176
- "@directus/system-data": "3.4.2",
177
- "@directus/validation": "2.0.12",
175
+ "@directus/storage-driver-s3": "12.0.12",
178
176
  "@directus/utils": "13.0.13",
179
- "directus": "11.13.3"
177
+ "@directus/validation": "2.0.12",
178
+ "@directus/system-data": "3.4.2",
179
+ "directus": "11.13.4"
180
180
  },
181
181
  "devDependencies": {
182
182
  "@directus/tsconfig": "3.0.0",
@@ -219,8 +219,8 @@
219
219
  "knex-mock-client": "3.0.2",
220
220
  "typescript": "5.9.3",
221
221
  "vitest": "3.2.4",
222
- "@directus/types": "13.4.0",
223
- "@directus/schema-builder": "0.0.9"
222
+ "@directus/schema-builder": "0.0.9",
223
+ "@directus/types": "13.4.0"
224
224
  },
225
225
  "optionalDependencies": {
226
226
  "@keyv/redis": "3.0.1",
@@ -1,8 +0,0 @@
1
- /**
2
- * Generate callback URL from origin
3
- *
4
- * @param string Origin URL
5
- * @param providerName OAuth provider name
6
- * @returns url
7
- */
8
- export declare function generateCallbackUrl(providerName: string, originUrl: string): string;
@@ -1,11 +0,0 @@
1
- import { Url } from '../../utils/url.js';
2
- /**
3
- * Generate callback URL from origin
4
- *
5
- * @param string Origin URL
6
- * @param providerName OAuth provider name
7
- * @returns url
8
- */
9
- export function generateCallbackUrl(providerName, originUrl) {
10
- return new Url(originUrl).addPath('auth', 'login', providerName, 'callback').toString();
11
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Check if the redirect URL is allowed
3
- * @param originUrl Origin URL
4
- * @param provider OAuth provider name
5
- * @param redirect URL to redirect to
6
- * @returns True if the redirect is allowed, false otherwise
7
- */
8
- export declare function isLoginRedirectAllowed(provider: string, originUrl: string, redirect: unknown): boolean;