Skip to content

Commit d893c38

Browse files
enable and fix dpop
1 parent 47b7e8c commit d893c38

File tree

17 files changed

+174
-64
lines changed

17 files changed

+174
-64
lines changed

.github/workflows/roundtrip/demo-idp.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
: "${KC_VERSION:=24.0.3}"
32
: "${KC_BROWSERTEST_CLIENT_SECRET:=$(uuidgen)}"
43

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

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

11-
kcadm.sh config credentials --server http://localhost:65432/auth --realm master --user admin << EOF
10+
kcadm.sh config credentials --server http://localhost:65432/auth --realm master --user admin <<EOF
1211
changeme
1312
EOF
1413

15-
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
14+
kcadm.sh create clients -r opentdf \
15+
-s clientId=browsertest \
16+
-s enabled=true \
17+
-s 'redirectUris=["http://localhost:65432/"]' \
18+
-s consentRequired=false \
19+
-s standardFlowEnabled=true \
20+
-s directAccessGrantsEnabled=true \
21+
-s serviceAccountsEnabled=false \
22+
-s publicClient=true \
23+
-s protocol=openid-connect \
24+
-s 'protocolMappers=[{"name":"aud","protocol":"openid-connect","protocolMapper":"oidc-audience-mapper","consentRequired":false,"config":{"access.token.claim":"true","included.custom.audience":"http://localhost:65432"}}]' \
25+
-s 'attributes={"dpop.bound.access.tokens":"true"}'
26+
1627
kcadm.sh create users -r opentdf -s username=user1 -s enabled=true
1728
kcadm.sh set-password -r opentdf --username user1 --new-password testuser123
1829

.github/workflows/roundtrip/docker-compose.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
services:
22
keycloak:
33
# This is kc 24.0.1 with opentdf protocol mapper on board
4-
image: ghcr.io/opentdf/keycloak:sha-8a6d35a
4+
image: quay.io/keycloak/keycloak:24.0
55
restart: always
6-
command: ["start-dev", "--log-level=DEBUG"]
6+
# To enable debugging, use this CMD and also set and expose the DEBUG_PORT
7+
# command: ["--debug", "start-dev", "--log-level=DEBUG"]
78
environment:
9+
# DEBUG_PORT: "*:30012"
810
KC_DB_VENDOR: postgres
911
KC_DB_URL_HOST: keycloakdb
1012
KC_DB_URL_PORT: 5432
@@ -27,6 +29,7 @@ services:
2729
KEYCLOAK_ADMIN_PASSWORD: changeme
2830
ports:
2931
- "8888:8888"
32+
# - "30012:30012"
3033
healthcheck:
3134
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']
3235
interval: 5s

lib/src/auth/auth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export async function reqSignature(
8080
* ephemeral key, to be included in
8181
* [the claims object](https://github.com/opentdf/spec/blob/main/schema/ClaimsObject.md).
8282
*/
83-
export abstract class AuthProvider {
83+
export type AuthProvider = {
8484
/**
8585
* This function should be called if the consumer of this auth provider
8686
* changes the client keypair, or wishes to set the keypair after creating
@@ -94,15 +94,15 @@ export abstract class AuthProvider {
9494
* @param signingKey the client signing key pair. Will be bound
9595
* to the OIDC token and require a DPoP header, when set.
9696
*/
97-
abstract updateClientPublicKey(clientPubkey: string, signingKey?: CryptoKeyPair): Promise<void>;
97+
updateClientPublicKey(clientPubkey: string, signingKey?: CryptoKeyPair): Promise<void>;
9898

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

107107
/**
108108
* An AuthProvider encapsulates all logic necessary to authenticate to a backend service, in the

lib/src/auth/oidc-externaljwt-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AuthProvider, type HttpRequest } from './auth.js';
1+
import { type AuthProvider, type HttpRequest } from './auth.js';
22
import { AccessToken, type ExternalJwtCredentials } from './oidc.js';
33

44
export class OIDCExternalJwtProvider implements AuthProvider {

lib/src/auth/oidc-refreshtoken-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AuthProvider, type HttpRequest } from './auth.js';
1+
import { type AuthProvider, type HttpRequest } from './auth.js';
22
import { AccessToken, type RefreshTokenCredentials } from './oidc.js';
33

44
export class OIDCRefreshTokenProvider implements AuthProvider {

lib/src/auth/providers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from './oidc.js';
77
import { OIDCClientCredentialsProvider } from './oidc-clientcredentials-provider.js';
88
import { OIDCExternalJwtProvider } from './oidc-externaljwt-provider.js';
9-
import { AuthProvider } from './auth.js';
9+
import { type AuthProvider } from './auth.js';
1010
import { OIDCRefreshTokenProvider } from './oidc-refreshtoken-provider.js';
1111
import { isBrowser } from '../utils.js';
1212

lib/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from './nanotdf/index.js';
1111
import { keyAgreement, extractPublicFromCertToCrypto } from './nanotdf-crypto/index.js';
1212
import { TypedArray, createAttribute, Policy } from './tdf/index.js';
13-
import { AuthProvider } from './auth/auth.js';
13+
import { type AuthProvider } from './auth/auth.js';
1414

1515
async function fetchKasPubKey(kasUrl: string): Promise<string> {
1616
const kasPubKeyResponse = await fetch(`${kasUrl}/kas_public_key?algorithm=ec:secp256r1`);

lib/src/kas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AuthProvider } from './auth/auth.js';
1+
import { type AuthProvider } from './auth/auth.js';
22

33
export class RewrapRequest {
44
signedRequestToken = '';

lib/tdf3/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ import {
2424
SplitKey,
2525
type EncryptionInformation,
2626
} from './src/models/encryption-information.js';
27-
import { AuthProvider, AppIdAuthProvider, type HttpMethod, HttpRequest } from '../src/auth/auth.js';
27+
import {
28+
AuthProvider,
29+
AppIdAuthProvider,
30+
type HttpMethod,
31+
HttpRequest,
32+
withHeaders,
33+
} from '../src/auth/auth.js';
2834
import { AesGcmCipher } from './src/ciphers/aes-gcm-cipher.js';
2935
import {
3036
NanoTDFClient,
@@ -39,6 +45,7 @@ import { type Chunker } from './src/utils/chunkers.js';
3945
export type {
4046
AlgorithmName,
4147
AlgorithmUrn,
48+
AuthProvider,
4249
Chunker,
4350
CryptoService,
4451
DecryptResult,
@@ -55,7 +62,6 @@ export {
5562
AesGcmCipher,
5663
Algorithms,
5764
AppIdAuthProvider,
58-
AuthProvider,
5965
AuthProviders,
6066
Binary,
6167
Client,
@@ -77,6 +83,7 @@ export {
7783
TDF3Client,
7884
clientType,
7985
createSessionKeys,
86+
withHeaders,
8087
version,
8188
};
8289

lib/tdf3/src/client/index.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ import {
2424
import { OIDCRefreshTokenProvider } from '../../../src/auth/oidc-refreshtoken-provider.js';
2525
import { OIDCExternalJwtProvider } from '../../../src/auth/oidc-externaljwt-provider.js';
2626
import { CryptoService, PemKeyPair } from '../crypto/declarations.js';
27-
import { AuthProvider, AppIdAuthProvider, HttpRequest } from '../../../src/auth/auth.js';
27+
import {
28+
type AuthProvider,
29+
AppIdAuthProvider,
30+
HttpRequest,
31+
withHeaders,
32+
} from '../../../src/auth/auth.js';
2833
import EAS from '../../../src/auth/Eas.js';
2934
import { validateSecureUrl } from '../../../src/utils.js';
3035

@@ -119,6 +124,7 @@ export interface ClientConfig {
119124
organizationName?: string;
120125
clientId?: string;
121126
dpopEnabled?: boolean;
127+
dpopKeys?: Promise<CryptoKeyPair>;
122128
kasEndpoint?: string;
123129
/**
124130
* List of allowed KASes to connect to for rewrap requests.
@@ -152,18 +158,22 @@ export async function createSessionKeys({
152158
cryptoService,
153159
dpopEnabled,
154160
keypair,
161+
dpopKeys,
155162
}: {
156163
authProvider?: AuthProvider | AppIdAuthProvider;
157164
cryptoService: CryptoService;
158165
dpopEnabled?: boolean;
159166
keypair?: PemKeyPair;
167+
dpopKeys?: Promise<CryptoKeyPair>;
160168
}): Promise<SessionKeys> {
161169
//If clientconfig has keypair, assume auth provider was already set up with pubkey and bail
162170
const k2 =
163171
keypair ?? (await cryptoService.cryptoToPemPair(await cryptoService.generateKeyPair()));
164172
let signingKeys;
165173

166-
if (dpopEnabled) {
174+
if (dpopKeys) {
175+
signingKeys = await dpopKeys;
176+
} else if (dpopEnabled) {
167177
signingKeys = await crypto.subtle.generateKey(rsaPkcs1Sha256(), true, ['sign']);
168178
}
169179

@@ -251,7 +261,7 @@ export class Client {
251261
constructor(config: ClientConfig) {
252262
const clientConfig = { ...defaultClientConfig, ...config };
253263
this.cryptoService = clientConfig.cryptoService;
254-
this.dpopEnabled = !!clientConfig.dpopEnabled;
264+
this.dpopEnabled = !!(clientConfig.dpopEnabled || clientConfig.dpopKeys);
255265

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

@@ -316,16 +326,13 @@ export class Client {
316326
});
317327
}
318328
}
319-
if (clientConfig.keypair) {
320-
this.sessionKeys = Promise.resolve({ keypair: clientConfig.keypair });
321-
} else {
322-
this.sessionKeys = createSessionKeys({
323-
authProvider: this.authProvider,
324-
cryptoService: this.cryptoService,
325-
dpopEnabled: this.dpopEnabled,
326-
keypair: clientConfig.keypair,
327-
});
328-
}
329+
this.sessionKeys = createSessionKeys({
330+
authProvider: this.authProvider,
331+
cryptoService: this.cryptoService,
332+
dpopEnabled: this.dpopEnabled,
333+
dpopKeys: clientConfig.dpopKeys,
334+
keypair: clientConfig.keypair,
335+
});
329336
if (clientConfig.kasPublicKey) {
330337
this.kasPublicKey = Promise.resolve({
331338
url: this.kasEndpoint,
@@ -535,12 +542,14 @@ export class Client {
535542
}
536543
}
537544

545+
export type { AuthProvider };
546+
538547
export {
539-
AuthProvider,
540548
AppIdAuthProvider,
541549
DecryptParamsBuilder,
542550
DecryptSource,
543551
EncryptParamsBuilder,
544552
HttpRequest,
545553
fromDataSource,
554+
withHeaders,
546555
};

lib/tdf3/src/tdf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import { htmlWrapperTemplate } from './templates/index.js';
4848
// TODO: remove dependencies from ciphers so that we can open-source instead of relying on other Virtru libs
4949
import { AesGcmCipher } from './ciphers/index.js';
5050
import {
51-
AuthProvider,
51+
type AuthProvider,
5252
AppIdAuthProvider,
5353
HttpRequest,
5454
type HttpMethod,

lib/tdf3/src/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { toByteArray, fromByteArray } from 'base64-js';
2-
import { AppIdAuthProvider, AuthProvider } from '../../../src/auth/auth.js';
2+
import { AppIdAuthProvider, type AuthProvider } from '../../../src/auth/auth.js';
33
import * as WebCryptoService from '../crypto/index.js';
44
import { KeyInfo, SplitKey } from '../models/index.js';
55

lib/tests/web/nano-roundtrip.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from '@esm-bundle/chai';
22
import sinon from 'sinon';
3-
import { AuthProvider, HttpRequest, withHeaders } from '../../src/auth/auth.js';
3+
import { type AuthProvider, HttpRequest, withHeaders } from '../../src/auth/auth.js';
44

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

opentdf.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ services:
1515
enabled: true
1616
authorization:
1717
enabled: true
18-
url: http://localhost:8888
18+
url: http://localhost:65432
1919
client: "tdf-entity-resolution"
2020
secret: "secret"
2121
realm: "opentdf"
2222
legacy: true
2323
server:
2424
auth:
2525
enabled: true
26-
audience: "http://localhost:8080"
27-
issuer: http://localhost:8888/auth/realms/opentdf
26+
audience: "http://localhost:65432"
27+
issuer: http://localhost:65432/auth/realms/opentdf
2828
clients:
2929
- "opentdf"
3030
- "opentdf-sdk"

web-app/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)