Skip to content

Commit

Permalink
enable and fix dpop
Browse files Browse the repository at this point in the history
  • Loading branch information
dmihalcik-virtru committed Apr 23, 2024
1 parent 47b7e8c commit d893c38
Show file tree
Hide file tree
Showing 17 changed files with 174 additions and 64 deletions.
17 changes: 14 additions & 3 deletions .github/workflows/roundtrip/demo-idp.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

: "${KC_VERSION:=24.0.3}"
: "${KC_BROWSERTEST_CLIENT_SECRET:=$(uuidgen)}"

Expand All @@ -8,11 +7,23 @@ unzip kc.zip -d keycloak-${KC_VERSION}

export PATH=$PATH:$(pwd)/keycloak-${KC_VERSION}/bin

kcadm.sh config credentials --server http://localhost:65432/auth --realm master --user admin << EOF
kcadm.sh config credentials --server http://localhost:65432/auth --realm master --user admin <<EOF
changeme
EOF

kcadm.sh create clients -r opentdf -s clientId=browsertest -s enabled=true -s 'redirectUris=["http://localhost:65432/"]' -s consentRequired=false -s standardFlowEnabled=true -s directAccessGrantsEnabled=true -s serviceAccountsEnabled=false -s publicClient=true -s protocol=openid-connect
kcadm.sh create clients -r opentdf \
-s clientId=browsertest \
-s enabled=true \
-s 'redirectUris=["http://localhost:65432/"]' \
-s consentRequired=false \
-s standardFlowEnabled=true \
-s directAccessGrantsEnabled=true \
-s serviceAccountsEnabled=false \
-s publicClient=true \
-s protocol=openid-connect \
-s 'protocolMappers=[{"name":"aud","protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","consentRequired":false,"config":{"access.token.claim":"true","included.custom.audience":"http://localhost:65432"}}]' \
-s 'attributes={"dpop.bound.access.tokens":"true"}'

kcadm.sh create users -r opentdf -s username=user1 -s enabled=true
kcadm.sh set-password -r opentdf --username user1 --new-password testuser123

Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/roundtrip/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
services:
keycloak:
# This is kc 24.0.1 with opentdf protocol mapper on board
image: ghcr.io/opentdf/keycloak:sha-8a6d35a
image: quay.io/keycloak/keycloak:24.0
restart: always
command: ["start-dev", "--log-level=DEBUG"]
# To enable debugging, use this CMD and also set and expose the DEBUG_PORT
# command: ["--debug", "start-dev", "--log-level=DEBUG"]
environment:
# DEBUG_PORT: "*:30012"
KC_DB_VENDOR: postgres
KC_DB_URL_HOST: keycloakdb
KC_DB_URL_PORT: 5432
Expand All @@ -27,6 +29,7 @@ services:
KEYCLOAK_ADMIN_PASSWORD: changeme
ports:
- "8888:8888"
# - "30012:30012"
healthcheck:
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:8888/auth/health/live']
interval: 5s
Expand Down
8 changes: 4 additions & 4 deletions lib/src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export async function reqSignature(
* ephemeral key, to be included in
* [the claims object](https://github.com/opentdf/spec/blob/main/schema/ClaimsObject.md).
*/
export abstract class AuthProvider {
export type AuthProvider = {
/**
* This function should be called if the consumer of this auth provider
* changes the client keypair, or wishes to set the keypair after creating
Expand All @@ -94,15 +94,15 @@ export abstract class AuthProvider {
* @param signingKey the client signing key pair. Will be bound
* to the OIDC token and require a DPoP header, when set.
*/
abstract updateClientPublicKey(clientPubkey: string, signingKey?: CryptoKeyPair): Promise<void>;
updateClientPublicKey(clientPubkey: string, signingKey?: CryptoKeyPair): Promise<void>;

/**
* Augment the provided http request with custom auth info to be used by backend services.
*
* @param httpReq - Required. An http request pre-populated with the data public key.
*/
abstract withCreds(httpReq: HttpRequest): Promise<HttpRequest>;
}
withCreds(httpReq: HttpRequest): Promise<HttpRequest>;
};

/**
* An AuthProvider encapsulates all logic necessary to authenticate to a backend service, in the
Expand Down
2 changes: 1 addition & 1 deletion lib/src/auth/oidc-externaljwt-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthProvider, type HttpRequest } from './auth.js';
import { type AuthProvider, type HttpRequest } from './auth.js';
import { AccessToken, type ExternalJwtCredentials } from './oidc.js';

export class OIDCExternalJwtProvider implements AuthProvider {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/auth/oidc-refreshtoken-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthProvider, type HttpRequest } from './auth.js';
import { type AuthProvider, type HttpRequest } from './auth.js';
import { AccessToken, type RefreshTokenCredentials } from './oidc.js';

export class OIDCRefreshTokenProvider implements AuthProvider {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/auth/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from './oidc.js';
import { OIDCClientCredentialsProvider } from './oidc-clientcredentials-provider.js';
import { OIDCExternalJwtProvider } from './oidc-externaljwt-provider.js';
import { AuthProvider } from './auth.js';
import { type AuthProvider } from './auth.js';
import { OIDCRefreshTokenProvider } from './oidc-refreshtoken-provider.js';
import { isBrowser } from '../utils.js';

Expand Down
2 changes: 1 addition & 1 deletion lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from './nanotdf/index.js';
import { keyAgreement, extractPublicFromCertToCrypto } from './nanotdf-crypto/index.js';
import { TypedArray, createAttribute, Policy } from './tdf/index.js';
import { AuthProvider } from './auth/auth.js';
import { type AuthProvider } from './auth/auth.js';

async function fetchKasPubKey(kasUrl: string): Promise<string> {
const kasPubKeyResponse = await fetch(`${kasUrl}/kas_public_key?algorithm=ec:secp256r1`);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/kas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AuthProvider } from './auth/auth.js';
import { type AuthProvider } from './auth/auth.js';

export class RewrapRequest {
signedRequestToken = '';
Expand Down
11 changes: 9 additions & 2 deletions lib/tdf3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ import {
SplitKey,
type EncryptionInformation,
} from './src/models/encryption-information.js';
import { AuthProvider, AppIdAuthProvider, type HttpMethod, HttpRequest } from '../src/auth/auth.js';
import {
AuthProvider,
AppIdAuthProvider,
type HttpMethod,
HttpRequest,
withHeaders,
} from '../src/auth/auth.js';
import { AesGcmCipher } from './src/ciphers/aes-gcm-cipher.js';
import {
NanoTDFClient,
Expand All @@ -39,6 +45,7 @@ import { type Chunker } from './src/utils/chunkers.js';
export type {
AlgorithmName,
AlgorithmUrn,
AuthProvider,
Chunker,
CryptoService,
DecryptResult,
Expand All @@ -55,7 +62,6 @@ export {
AesGcmCipher,
Algorithms,
AppIdAuthProvider,
AuthProvider,
AuthProviders,
Binary,
Client,
Expand All @@ -77,6 +83,7 @@ export {
TDF3Client,
clientType,
createSessionKeys,
withHeaders,
version,
};

Expand Down
37 changes: 23 additions & 14 deletions lib/tdf3/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import {
import { OIDCRefreshTokenProvider } from '../../../src/auth/oidc-refreshtoken-provider.js';
import { OIDCExternalJwtProvider } from '../../../src/auth/oidc-externaljwt-provider.js';
import { CryptoService, PemKeyPair } from '../crypto/declarations.js';
import { AuthProvider, AppIdAuthProvider, HttpRequest } from '../../../src/auth/auth.js';
import {
type AuthProvider,
AppIdAuthProvider,
HttpRequest,
withHeaders,
} from '../../../src/auth/auth.js';
import EAS from '../../../src/auth/Eas.js';
import { validateSecureUrl } from '../../../src/utils.js';

Expand Down Expand Up @@ -119,6 +124,7 @@ export interface ClientConfig {
organizationName?: string;
clientId?: string;
dpopEnabled?: boolean;
dpopKeys?: Promise<CryptoKeyPair>;
kasEndpoint?: string;
/**
* List of allowed KASes to connect to for rewrap requests.
Expand Down Expand Up @@ -152,18 +158,22 @@ export async function createSessionKeys({
cryptoService,
dpopEnabled,
keypair,
dpopKeys,
}: {
authProvider?: AuthProvider | AppIdAuthProvider;
cryptoService: CryptoService;
dpopEnabled?: boolean;
keypair?: PemKeyPair;
dpopKeys?: Promise<CryptoKeyPair>;
}): Promise<SessionKeys> {
//If clientconfig has keypair, assume auth provider was already set up with pubkey and bail
const k2 =
keypair ?? (await cryptoService.cryptoToPemPair(await cryptoService.generateKeyPair()));
let signingKeys;

if (dpopEnabled) {
if (dpopKeys) {
signingKeys = await dpopKeys;
} else if (dpopEnabled) {
signingKeys = await crypto.subtle.generateKey(rsaPkcs1Sha256(), true, ['sign']);
}

Expand Down Expand Up @@ -251,7 +261,7 @@ export class Client {
constructor(config: ClientConfig) {
const clientConfig = { ...defaultClientConfig, ...config };
this.cryptoService = clientConfig.cryptoService;
this.dpopEnabled = !!clientConfig.dpopEnabled;
this.dpopEnabled = !!(clientConfig.dpopEnabled || clientConfig.dpopKeys);

clientConfig.readerUrl && (this.readerUrl = clientConfig.readerUrl);

Expand Down Expand Up @@ -316,16 +326,13 @@ export class Client {
});
}
}
if (clientConfig.keypair) {
this.sessionKeys = Promise.resolve({ keypair: clientConfig.keypair });
} else {
this.sessionKeys = createSessionKeys({
authProvider: this.authProvider,
cryptoService: this.cryptoService,
dpopEnabled: this.dpopEnabled,
keypair: clientConfig.keypair,
});
}
this.sessionKeys = createSessionKeys({
authProvider: this.authProvider,
cryptoService: this.cryptoService,
dpopEnabled: this.dpopEnabled,
dpopKeys: clientConfig.dpopKeys,
keypair: clientConfig.keypair,
});
if (clientConfig.kasPublicKey) {
this.kasPublicKey = Promise.resolve({
url: this.kasEndpoint,
Expand Down Expand Up @@ -535,12 +542,14 @@ export class Client {
}
}

export type { AuthProvider };

export {
AuthProvider,
AppIdAuthProvider,
DecryptParamsBuilder,
DecryptSource,
EncryptParamsBuilder,
HttpRequest,
fromDataSource,
withHeaders,
};
2 changes: 1 addition & 1 deletion lib/tdf3/src/tdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { htmlWrapperTemplate } from './templates/index.js';
// TODO: remove dependencies from ciphers so that we can open-source instead of relying on other Virtru libs
import { AesGcmCipher } from './ciphers/index.js';
import {
AuthProvider,
type AuthProvider,
AppIdAuthProvider,
HttpRequest,
type HttpMethod,
Expand Down
2 changes: 1 addition & 1 deletion lib/tdf3/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { toByteArray, fromByteArray } from 'base64-js';
import { AppIdAuthProvider, AuthProvider } from '../../../src/auth/auth.js';
import { AppIdAuthProvider, type AuthProvider } from '../../../src/auth/auth.js';
import * as WebCryptoService from '../crypto/index.js';
import { KeyInfo, SplitKey } from '../models/index.js';

Expand Down
2 changes: 1 addition & 1 deletion lib/tests/web/nano-roundtrip.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from '@esm-bundle/chai';
import sinon from 'sinon';
import { AuthProvider, HttpRequest, withHeaders } from '../../src/auth/auth.js';
import { type AuthProvider, HttpRequest, withHeaders } from '../../src/auth/auth.js';

import { NanoTDFClient } from '../../src/index.js';

Expand Down
6 changes: 3 additions & 3 deletions opentdf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ services:
enabled: true
authorization:
enabled: true
url: http://localhost:8888
url: http://localhost:65432
client: "tdf-entity-resolution"
secret: "secret"
realm: "opentdf"
legacy: true
server:
auth:
enabled: true
audience: "http://localhost:8080"
issuer: http://localhost:8888/auth/realms/opentdf
audience: "http://localhost:65432"
issuer: http://localhost:65432/auth/realms/opentdf
clients:
- "opentdf"
- "opentdf-sdk"
Expand Down
4 changes: 2 additions & 2 deletions web-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d893c38

Please sign in to comment.