mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-29 22:02:57 +00:00
Modify service token format
This commit is contained in:
backend/src
controllers/v1
helpers
middleware
models
routes
@ -15,7 +15,7 @@ import {
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getServiceTokenData = async (req: Request, res: Response) => ({
|
||||
export const getServiceTokenData = async (req: Request, res: Response) => res.status(200).send({
|
||||
serviceTokenData: req.serviceTokenData
|
||||
});
|
||||
|
||||
@ -38,35 +38,32 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
tag,
|
||||
expiresIn
|
||||
} = req.body;
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('hex');
|
||||
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
|
||||
|
||||
// create 41-char service token with first 9-char being the prefix
|
||||
serviceToken = `st.${crypto.randomBytes(19).toString('hex')}`;
|
||||
|
||||
const serviceTokenHash = await bcrypt.hash(serviceToken, SALT_ROUNDS);
|
||||
|
||||
// compute access token expiration date
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
|
||||
// create service token data
|
||||
serviceTokenData = new ServiceTokenData({
|
||||
serviceTokenData = await new ServiceTokenData({
|
||||
name,
|
||||
workspace: workspaceId,
|
||||
environment,
|
||||
user: req.user._id,
|
||||
expiresAt,
|
||||
prefix: serviceToken.substring(0, 9),
|
||||
serviceTokenHash,
|
||||
secretHash,
|
||||
encryptedKey,
|
||||
iv,
|
||||
tag
|
||||
})
|
||||
}).save();
|
||||
|
||||
await serviceTokenData.save();
|
||||
|
||||
// return service token data without sensitive data
|
||||
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
|
||||
|
||||
if (!serviceTokenData) throw new Error('Failed to find service token data');
|
||||
|
||||
serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
|
||||
|
||||
} catch (err) {
|
||||
Sentry.setUser({ email: req.user.email });
|
||||
Sentry.captureException(err);
|
||||
|
@ -15,7 +15,8 @@ import {
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
ServiceTokenDataNotFoundError,
|
||||
UnauthorizedRequestError
|
||||
UnauthorizedRequestError,
|
||||
BadRequestError
|
||||
} from '../utils/errors';
|
||||
|
||||
/**
|
||||
@ -101,15 +102,30 @@ const getAuthSTDPayload = async ({
|
||||
}) => {
|
||||
let serviceTokenData;
|
||||
try {
|
||||
const serviceTokenHash = await bcrypt.hash(authTokenValue, SALT_ROUNDS);
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||
|
||||
// TODO: optimize double query
|
||||
serviceTokenData = await ServiceTokenData
|
||||
.findById(TOKEN_IDENTIFIER, 'secretHash expiresAt');
|
||||
|
||||
if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
||||
// case: service token expired
|
||||
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
||||
throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate expired service token'
|
||||
});
|
||||
}
|
||||
|
||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||
|
||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
||||
if (!isMatch) throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate service token'
|
||||
});
|
||||
|
||||
serviceTokenData = await ServiceTokenData
|
||||
.findOne({
|
||||
serviceTokenHash
|
||||
})
|
||||
.findById(TOKEN_IDENTIFIER)
|
||||
.select('+encryptedKey +iv +tag');
|
||||
|
||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||
|
||||
} catch (err) {
|
||||
throw UnauthorizedRequestError({
|
||||
|
@ -6,8 +6,7 @@ import {
|
||||
getAuthUserPayload,
|
||||
getAuthSTDPayload
|
||||
} from '../helpers/auth';
|
||||
import { JWT_AUTH_SECRET } from '../config';
|
||||
import { AccountNotFoundError, BadRequestError, UnauthorizedRequestError } from '../utils/errors';
|
||||
import { BadRequestError } from '../utils/errors';
|
||||
|
||||
declare module 'jsonwebtoken' {
|
||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||
|
@ -40,10 +40,10 @@ const requireWorkspaceAuth = ({
|
||||
if (
|
||||
req.serviceTokenData
|
||||
&& req.serviceTokenData.workspace !== workspaceId
|
||||
&& req.serviceTokenData.environment !== req.body.environment
|
||||
)
|
||||
// case: st auth
|
||||
&& req.serviceTokenData.environment !== req.query.environment
|
||||
) {
|
||||
next(UnauthorizedRequestError({message: 'Unable to authenticate workspace'}))
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (err) {
|
||||
|
@ -7,8 +7,7 @@ export interface IServiceTokenData {
|
||||
environment: string; // TODO: adapt to upcoming environment id
|
||||
user: Types.ObjectId;
|
||||
expiresAt: Date;
|
||||
prefix: string;
|
||||
serviceTokenHash: string;
|
||||
secretHash: string;
|
||||
encryptedKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
@ -37,11 +36,7 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
|
||||
expiresAt: {
|
||||
type: Date
|
||||
},
|
||||
prefix: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
serviceTokenHash: {
|
||||
secretHash: {
|
||||
type: String,
|
||||
unique: true,
|
||||
required: true,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireAuth,
|
||||
@ -20,8 +20,6 @@ router.get(
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['serviceToken']
|
||||
}),
|
||||
param('serviceTokenDataId').exists().trim(),
|
||||
validateRequest,
|
||||
serviceTokenDataController.getServiceTokenData
|
||||
);
|
||||
|
||||
|
@ -4,11 +4,9 @@ import { body, param, query } from 'express-validator';
|
||||
import {
|
||||
requireAuth,
|
||||
requireWorkspaceAuth,
|
||||
requireServiceTokenAuth,
|
||||
validateRequest
|
||||
} from '../../middleware';
|
||||
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables';
|
||||
import { membershipController } from '../../controllers/v1';
|
||||
import { workspaceController } from '../../controllers/v2';
|
||||
|
||||
router.post(
|
||||
|
Reference in New Issue
Block a user