1895 extensibility v1 application tokens 3 (#16504)
- moves applicationRoleId to application entity - add new `APPLICATION` FieldActorSource and `APPLICATION` JwtTokenTypeEnum value - create a new token with applicationId when executing a function - when applicationId is in token, check for application.defaultRole permissions -use twenty-shared types in `twenty-sdk/application` - create a new import from generate called "Twenty" that you can use directly without having to set TWENTY_API_KEY AND TWENTY_API_URL (keep metadata or core parameter only) - provide to serverless unique one time BEARER TOKEN to run it Result <img width="977" height="566" alt="image" src="https://github.com/user-attachments/assets/e78428a0-5b13-4975-aa13-58ee3b32450c" /> <img width="910" height="596" alt="image" src="https://github.com/user-attachments/assets/6ec72bf5-7655-4093-a45e-ad269595a324" /> <img width="741" height="568" alt="image" src="https://github.com/user-attachments/assets/7683944c-fd79-4417-8fb2-8e4815cc112f" />
This commit is contained in:
+46
-38
@@ -17,13 +17,8 @@ import {
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import {
|
||||
type AccessTokenJwtPayload,
|
||||
type FileTokenJwtPayload,
|
||||
type JwtPayload,
|
||||
JwtTokenTypeEnum,
|
||||
type RefreshTokenJwtPayload,
|
||||
type TransientTokenJwtPayload,
|
||||
type WorkspaceAgnosticTokenJwtPayload,
|
||||
} from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
|
||||
@@ -54,16 +49,10 @@ export class JwtWrapperService {
|
||||
|
||||
verifyJwtToken(
|
||||
token: string,
|
||||
type: JwtTokenTypeEnum,
|
||||
options?: JwtVerifyOptions,
|
||||
isLegacyApiKey = false,
|
||||
) {
|
||||
const payload = this.decode<
|
||||
| TransientTokenJwtPayload
|
||||
| RefreshTokenJwtPayload
|
||||
| WorkspaceAgnosticTokenJwtPayload
|
||||
| AccessTokenJwtPayload
|
||||
| FileTokenJwtPayload
|
||||
>(token, {
|
||||
const payload = this.decode<JwtPayload>(token, {
|
||||
json: true,
|
||||
});
|
||||
|
||||
@@ -71,37 +60,56 @@ export class JwtWrapperService {
|
||||
throw new AuthException('No payload', AuthExceptionCode.UNAUTHENTICATED);
|
||||
}
|
||||
|
||||
// @TODO: Migrate to use type from payload instead of parameter
|
||||
type =
|
||||
payload.type === JwtTokenTypeEnum.WORKSPACE_AGNOSTIC
|
||||
? JwtTokenTypeEnum.WORKSPACE_AGNOSTIC
|
||||
: type;
|
||||
const type = payload.type;
|
||||
|
||||
// TODO: check if this is really needed
|
||||
if (type !== 'FILE' && !payload.sub) {
|
||||
const appSecretBody =
|
||||
'workspaceId' in payload
|
||||
? payload.workspaceId
|
||||
: 'userId' in payload
|
||||
? payload.userId
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(appSecretBody)) {
|
||||
throw new AuthException(
|
||||
'No payload sub',
|
||||
AuthExceptionCode.UNAUTHENTICATED,
|
||||
'Invalid token type',
|
||||
AuthExceptionCode.INVALID_JWT_TOKEN_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: Deprecate this once old API KEY tokens are no longer in use
|
||||
if (!payload.type && !('workspaceId' in payload) && type === 'ACCESS') {
|
||||
// Supporting old API KEY tokens
|
||||
if (
|
||||
!payload.type &&
|
||||
!('workspaceId' in payload) &&
|
||||
type === JwtTokenTypeEnum.API_KEY
|
||||
) {
|
||||
return this.jwtService.verify(token, {
|
||||
...options,
|
||||
secret: this.generateAppSecretLegacy(),
|
||||
});
|
||||
}
|
||||
|
||||
const appSecretBody =
|
||||
'workspaceId' in payload ? payload.workspaceId : payload.userId;
|
||||
|
||||
if (!isDefined(appSecretBody)) {
|
||||
throw new AuthException(
|
||||
'Invalid token type',
|
||||
AuthExceptionCode.INVALID_JWT_TOKEN_TYPE,
|
||||
);
|
||||
// This is due to an unfortunate mistake in the secret generation of API_KEY
|
||||
// tokens. We used to sign with ACCESS Jwt Token Type instead of API_KEY.
|
||||
// Now we need to check both cases not to break the existing api keys
|
||||
// See this PR for context -> https://github.com/twentyhq/twenty/pull/16504
|
||||
// This code block can be deleted, but all api keys created before
|
||||
// 12/12/2025 will be broken
|
||||
if (type === JwtTokenTypeEnum.API_KEY && !isLegacyApiKey) {
|
||||
try {
|
||||
return this.jwtService.verify(token, {
|
||||
...options,
|
||||
secret: this.generateAppSecret(type, appSecretBody),
|
||||
});
|
||||
} catch {
|
||||
return this.jwtService.verify(token, {
|
||||
...options,
|
||||
secret: this.generateAppSecret(
|
||||
JwtTokenTypeEnum.ACCESS,
|
||||
appSecretBody,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.jwtService.verify(token, {
|
||||
@@ -114,17 +122,17 @@ export class JwtWrapperService {
|
||||
'Token has expired.',
|
||||
AuthExceptionCode.UNAUTHENTICATED,
|
||||
);
|
||||
} else if (error instanceof jwt.JsonWebTokenError) {
|
||||
}
|
||||
if (error instanceof jwt.JsonWebTokenError) {
|
||||
throw new AuthException(
|
||||
'Token invalid.',
|
||||
AuthExceptionCode.UNAUTHENTICATED,
|
||||
);
|
||||
} else {
|
||||
throw new AuthException(
|
||||
'Unknown token error.',
|
||||
AuthExceptionCode.INVALID_INPUT,
|
||||
);
|
||||
}
|
||||
throw new AuthException(
|
||||
'Unknown token error.',
|
||||
AuthExceptionCode.INVALID_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user