Skip to content

Commit

Permalink
(Feature) Allow user-device and team-device auth for calls through se…
Browse files Browse the repository at this point in the history
…cure tunnel (#268)

Adds a way to call API endpoints with a secure tunnel that have an
authentication method different from `app`
  • Loading branch information
jomi-se committed Jun 17, 2024
1 parent 6de2375 commit e10aa95
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/modules/auth/confidential-sso/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const doConfidentialSSOVerification = async ({ requestedLogin }: Confiden
...api,
path: 'authentication/RequestLogin2',
payload: { login: requestedLogin },
authentication: { type: 'app' },
});

const { idpAuthorizeUrl, spCallbackUrl, teamUuid, domainName } = requestLoginResponse;
Expand Down Expand Up @@ -56,6 +57,7 @@ export const doConfidentialSSOVerification = async ({ requestedLogin }: Confiden
...api,
path: 'authentication/ConfirmLogin2',
payload: { teamUuid, domainName, samlResponse },
authentication: { type: 'app' },
});

const ssoVerificationResult = await performSSOVerification({
Expand Down
48 changes: 37 additions & 11 deletions src/modules/tunnel-api-connect/steps/sendSecureContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { SecureContentRequest, SecureContentResponse, SendSecureContentPara
import { SecureTunnelNotInitialized, SendSecureContentDataDecryptionError } from '../errors.js';
import type { ApiConnectInternalParams, ApiData, ApiRequestsDefault } from '../types.js';
import { TypeCheck } from '../../typecheck/index.js';
import { requestAppApi } from '../../../requestApi.js';
import { requestAppApi, requestTeamApi, requestUserApi } from '../../../requestApi.js';

const verifySendSecureBodySchemaValidator = new TypeCheck<SecureContentResponse>(secureContentBodyDataSchema);

Expand All @@ -24,19 +24,45 @@ export const sendSecureContent = async <R extends ApiRequestsDefault>(
throw new SecureTunnelNotInitialized();
}

const { path, clientStateIn, clientStateOut, payload } = params;
const { path, clientStateIn, clientStateOut, payload: rawPayload, authentication = { type: 'app' } } = params;
const { tunnelUuid } = apiData.clientHello;

const encryptedData = encryptData(clientStateOut, payload);
const encryptedData = encryptData(clientStateOut, rawPayload);

const response = await requestAppApi<SecureContentResponse>({
path,
payload: {
encryptedData: sodium.to_hex(encryptedData),
tunnelUuid,
} satisfies SecureContentRequest,
isNitroEncryptionService: true,
});
const payload = {
encryptedData: sodium.to_hex(encryptedData),
tunnelUuid,
} satisfies SecureContentRequest;

let response: SecureContentResponse;

switch (authentication.type) {
case 'userDevice':
response = await requestUserApi<SecureContentResponse>({
path,
payload,
isNitroEncryptionService: true,
deviceKeys: authentication.deviceKeys,
login: authentication.login,
});
break;
case 'teamDevice':
response = await requestTeamApi<SecureContentResponse>({
path,
payload,
isNitroEncryptionService: true,
teamDeviceKeys: authentication.teamDeviceKeys,
teamUuid: authentication.teamUuid,
});
break;
case 'app':
response = await requestAppApi<SecureContentResponse>({
path,
payload,
isNitroEncryptionService: true,
});
break;
}

const body = verifySendSecureBodySchemaValidator.validate(response);
if (body instanceof Error) {
Expand Down
22 changes: 22 additions & 0 deletions src/modules/tunnel-api-connect/steps/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,33 @@ export interface TerminateHelloRequest {
tunnelUuid: string;
}

interface AppAuthenticationParams {
type: 'app';
}

interface UserDeviceAuthenticationParams {
type: 'userDevice';
login: string;
deviceKeys: { accessKey: string; secretKey: string };
}

interface TeamDeviceAuthenticationParams {
type: 'teamDevice';
teamUuid: string;
teamDeviceKeys: { accessKey: string; secretKey: string };
}

export type AuthenticationParams =
| AppAuthenticationParams
| UserDeviceAuthenticationParams
| TeamDeviceAuthenticationParams;

export interface SendSecureContentParams<R extends ApiRequestsDefault> {
path: R['path'];
clientStateIn: sodium.StateAddress;
clientStateOut: sodium.StateAddress;
payload: R['input'];
authentication?: AuthenticationParams;
}

export interface TerminateHelloParams {
Expand Down
2 changes: 1 addition & 1 deletion src/modules/tunnel-api-connect/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface ApiConnect {
makeOrRefreshSession: (params: RefreshSessionParams) => Promise<void>;
/** Reinitialize the tunnel when the session has expired (cookie) */
sendSecureContent: <R extends ApiRequestsDefault>(
params: Pick<SendSecureContentParams<R>, 'path' | 'payload'>
params: Pick<SendSecureContentParams<R>, 'path' | 'payload' | 'authentication'>
) => Promise<R['output']>;
}

Expand Down
2 changes: 2 additions & 0 deletions src/requestApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export interface RequestUserApi {
accessKey: string;
secretKey: string;
};
isNitroEncryptionService?: boolean;
}

export const requestUserApi = async <T>(params: RequestUserApi): Promise<T> => {
Expand All @@ -124,6 +125,7 @@ export interface RequestTeamApi {
accessKey: string;
secretKey: string;
};
isNitroEncryptionService?: boolean;
}

export const requestTeamApi = async <T>(params: RequestTeamApi): Promise<T> => {
Expand Down

0 comments on commit e10aa95

Please sign in to comment.