Merge pull request #4858 from Infisical/feat/adds-GETtokenAuthTokenById-api-endpoint

[ENG-4142] feat: adds GET endpoint for single auth token by ID
This commit is contained in:
Piyush Gupta
2025-11-20 21:09:59 +05:30
committed by GitHub
7 changed files with 128 additions and 6 deletions

View File

@@ -186,6 +186,7 @@ export enum EventType {
CREATE_TOKEN_IDENTITY_TOKEN_AUTH = "create-token-identity-token-auth",
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
GET_TOKEN_IDENTITY_TOKEN_AUTH = "get-token-identity-token-auth",
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
@@ -1029,6 +1030,15 @@ interface GetTokensIdentityTokenAuthEvent {
};
}
interface GetTokenIdentityTokenAuthEvent {
type: EventType.GET_TOKEN_IDENTITY_TOKEN_AUTH;
metadata: {
identityId: string;
identityName: string;
tokenId: string;
};
}
interface AddIdentityTokenAuthEvent {
type: EventType.ADD_IDENTITY_TOKEN_AUTH;
metadata: {
@@ -4214,6 +4224,7 @@ export type Event =
| CreateTokenIdentityTokenAuthEvent
| UpdateTokenIdentityTokenAuthEvent
| GetTokensIdentityTokenAuthEvent
| GetTokenIdentityTokenAuthEvent
| AddIdentityTokenAuthEvent
| UpdateIdentityTokenAuthEvent
| GetIdentityTokenAuthEvent

View File

@@ -584,6 +584,10 @@ export const TOKEN_AUTH = {
offset: "The offset to start from. If you enter 10, it will start from the 10th token.",
limit: "The number of tokens to return."
},
GET_TOKEN: {
identityId: "The ID of the machine identity to get the token for.",
tokenId: "The ID of the token to get metadata for."
},
CREATE_TOKEN: {
identityId: "The ID of the machine identity to create the token for.",
name: "The name of the token to create."

View File

@@ -314,7 +314,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
accessToken: z.string(),
expiresIn: z.coerce.number(),
accessTokenMaxTTL: z.coerce.number(),
tokenType: z.literal("Bearer")
tokenType: z.literal("Bearer"),
tokenData: IdentityAccessTokensSchema
})
}
},
@@ -346,7 +347,8 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
accessToken,
tokenType: "Bearer" as const,
expiresIn: identityTokenAuth.accessTokenTTL,
accessTokenMaxTTL: identityTokenAuth.accessTokenMaxTTL
accessTokenMaxTTL: identityTokenAuth.accessTokenMaxTTL,
tokenData: identityAccessToken
};
}
});
@@ -406,6 +408,60 @@ export const registerIdentityTokenAuthRouter = async (server: FastifyZodProvider
}
});
server.route({
method: "GET",
url: "/token-auth/identities/:identityId/tokens/:tokenId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.TokenAuth],
description: "Get token for machine identity with Token Auth",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().describe(TOKEN_AUTH.GET_TOKEN.identityId),
tokenId: z.string().describe(TOKEN_AUTH.GET_TOKEN.tokenId)
}),
response: {
200: z.object({
token: IdentityAccessTokensSchema
})
}
},
handler: async (req) => {
const { token, identityMembershipOrg } = await server.services.identityTokenAuth.getTokenAuthTokenById({
identityId: req.params.identityId,
tokenId: req.params.tokenId,
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
isActorSuperAdmin: isSuperAdmin(req.auth)
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: identityMembershipOrg.scopeOrgId,
event: {
type: EventType.GET_TOKEN_IDENTITY_TOKEN_AUTH,
metadata: {
identityId: token.identityId,
identityName: identityMembershipOrg.identity.name,
tokenId: token.id
}
}
});
return { token };
}
});
server.route({
method: "PATCH",
url: "/token-auth/tokens/:tokenId",

View File

@@ -18,7 +18,6 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
.where(filter)
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.IdentityAccessToken}.identityId`)
.select(selectAllTableCols(TableName.IdentityAccessToken))
.select(db.ref("name").withSchema(TableName.Identity))
.select(db.ref("orgId").withSchema(TableName.Identity).as("identityScopeOrgId"))
.first();

View File

@@ -38,6 +38,7 @@ import {
TAttachTokenAuthDTO,
TCreateTokenAuthTokenDTO,
TGetTokenAuthDTO,
TGetTokenAuthTokenByIdDTO,
TGetTokenAuthTokensDTO,
TRevokeTokenAuthDTO,
TRevokeTokenAuthTokenDTO,
@@ -618,6 +619,52 @@ export const identityTokenAuthServiceFactory = ({
return { tokens, identityMembershipOrg };
};
const getTokenAuthTokenById = async ({
tokenId,
identityId,
isActorSuperAdmin,
actorId,
actor,
actorAuthMethod,
actorOrgId
}: TGetTokenAuthTokenByIdDTO) => {
await validateIdentityUpdateForSuperAdminPrivileges(identityId, isActorSuperAdmin);
const identityMembershipOrg = await membershipIdentityDAL.getIdentityById({
scopeData: {
scope: AccessScope.Organization,
orgId: actorOrgId
},
identityId
});
if (!identityMembershipOrg) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
if (!identityMembershipOrg.identity.authMethods.includes(IdentityAuthMethod.TOKEN_AUTH)) {
throw new BadRequestError({
message: "The identity does not have Token Auth"
});
}
const { permission } = await permissionService.getOrgPermission({
scope: OrganizationActionScope.Any,
actor,
actorId,
orgId: identityMembershipOrg.scopeOrgId,
actorAuthMethod,
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
const token = await identityAccessTokenDAL.findOne({
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
[`${TableName.IdentityAccessToken}.authMethod` as "authMethod"]: IdentityAuthMethod.TOKEN_AUTH,
[`${TableName.IdentityAccessToken}.identityId` as "identityId"]: identityId
});
if (!token) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` });
return { token, identityMembershipOrg };
};
const updateTokenAuthToken = async ({
tokenId,
name,
@@ -797,6 +844,7 @@ export const identityTokenAuthServiceFactory = ({
revokeIdentityTokenAuth,
createTokenAuthToken,
getTokenAuthTokens,
getTokenAuthTokenById,
updateTokenAuthToken,
revokeTokenAuthToken
};

View File

@@ -40,6 +40,12 @@ export type TGetTokenAuthTokensDTO = {
isActorSuperAdmin?: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TGetTokenAuthTokenByIdDTO = {
tokenId: string;
identityId: string;
isActorSuperAdmin?: boolean;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateTokenAuthTokenDTO = {
tokenId: string;
name?: string;

View File

@@ -840,9 +840,7 @@ export type CreateTokenIdentityTokenAuthDTO = {
export type CreateTokenIdentityTokenAuthRes = {
accessToken: string;
tokenType: string;
expiresIn: number;
accessTokenMaxTTL: number;
tokenData: IdentityAccessToken;
};
export type UpdateTokenIdentityTokenAuthDTO = {