Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/sparkly-dragons-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@forgerock/davinci-client': minor
'@forgerock/sdk-types': minor
---

Adds FIDO feature module to `@forgerock/davinci-client` package
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"ci:release": "pnpm publish -r --no-git-checks && changeset tag",
"ci:version": "changeset version && pnpm install --no-frozen-lockfile && pnpm nx format:write --uncommitted",
"circular-dep-check": "madge --circular .",
"clean": "shx rm -rf ./{coverage,dist,docs,node_modules,tmp}/ ./{packages,e2e}/*/{dist,node_modules}/ && git clean -fX -e \"!.env*,nx-cloud.env\" -e \"!**/GEMINI.md\"",
"clean": "shx rm -rf ./{coverage,dist,docs,node_modules,tmp}/ ./{packages,e2e}/*/{dist,node_modules}/ ./e2e/node_modules/ && git clean -fX -e \"!.env*,nx-cloud.env\" -e \"!**/GEMINI.md\"",
"commit": "git cz",
"commitlint": "commitlint --edit",
"create-package": "nx g @nx/js:library",
Expand Down
1 change: 1 addition & 0 deletions packages/davinci-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@forgerock/sdk-types": "workspace:*",
"@forgerock/storage": "workspace:*",
"@reduxjs/toolkit": "catalog:",
"effect": "catalog:effect",
"immer": "catalog:"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion packages/davinci-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
* of the MIT license. See the LICENSE file for details.
*/
import { davinci } from './lib/client.store.js';
import { fido } from './lib/fido/fido.js';

export { davinci };
export { davinci, fido };
2 changes: 1 addition & 1 deletion packages/davinci-client/src/lib/client.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import type {
Validator,
} from './client.types.js';
import { returnValidator } from './collector.utils.js';
import { ContinueNode, StartNode } from './node.types.js';
import type { ContinueNode, StartNode } from './node.types.js';

/**
* Create a client function that returns a set of methods
Expand Down
23 changes: 20 additions & 3 deletions packages/davinci-client/src/lib/client.types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import type { GenericError } from '@forgerock/sdk-types';

import type { InitFlow, InternalErrorResponse, Updater } from './client.types.js';
import type { ErrorNode, FailureNode, ContinueNode, StartNode, SuccessNode } from './node.types.js';
import type { PhoneNumberInputValue } from './collector.types.js';
import type {
FidoAuthenticationInputValue,
FidoRegistrationInputValue,
PhoneNumberInputValue,
} from './collector.types.js';

describe('Client Types', () => {
it('should allow function returning error', async () => {
Expand Down Expand Up @@ -170,15 +174,28 @@ describe('Client Types', () => {
describe('Updater', () => {
it('should accept string value and optional index', () => {
const updater: Updater = (
value: string | string[] | boolean | PhoneNumberInputValue,
value:
| string
| string[]
| PhoneNumberInputValue
| FidoRegistrationInputValue
| FidoAuthenticationInputValue,
index?: number,
) => {
return {
error: { message: 'Invalid value', code: 'INVALID', type: 'state_error' },
type: 'internal_error',
};
};
expectTypeOf(updater).parameter(0).toEqualTypeOf<string | string[] | PhoneNumberInputValue>();
expectTypeOf(updater)
.parameter(0)
.toEqualTypeOf<
| string
| string[]
| PhoneNumberInputValue
| FidoRegistrationInputValue
| FidoAuthenticationInputValue
>();
expectTypeOf(updater).parameter(1).toBeNullable();
expectTypeOf(updater).parameter(1).toBeNullable();
});
Expand Down
13 changes: 11 additions & 2 deletions packages/davinci-client/src/lib/client.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
*/
import type { GenericError } from '@forgerock/sdk-types';

import type { PhoneNumberInputValue } from './collector.types.js';
import type {
FidoRegistrationInputValue,
FidoAuthenticationInputValue,
PhoneNumberInputValue,
} from './collector.types.js';
import type { ErrorNode, FailureNode, ContinueNode, StartNode, SuccessNode } from './node.types.js';

export type FlowNode = ContinueNode | ErrorNode | StartNode | SuccessNode | FailureNode;
Expand All @@ -19,7 +23,12 @@ export interface InternalErrorResponse {
export type InitFlow = () => Promise<FlowNode | InternalErrorResponse>;

export type Updater = (
value: string | string[] | PhoneNumberInputValue,
value:
| string
| string[]
| PhoneNumberInputValue
| FidoRegistrationInputValue
| FidoAuthenticationInputValue,
index?: number,
) => InternalErrorResponse | null;
export type Validator = (value: string) =>
Expand Down
67 changes: 55 additions & 12 deletions packages/davinci-client/src/lib/collector.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* of the MIT license. See the LICENSE file for details.
*/

import type { FidoAuthenticationOptions, FidoRegistrationOptions } from './davinci.types.js';

/** *********************************************************************
* SINGLE-VALUE COLLECTORS
*/
Expand Down Expand Up @@ -302,14 +304,6 @@ export interface PhoneNumberOutputValue {
phoneNumber?: string;
}

export interface FidoRegistrationInputValue {
attestationValue?: PublicKeyCredential;
}

export interface FidoAuthenticationInputValue {
assertionValue?: PublicKeyCredential;
}

export interface ObjectOptionsCollectorWithStringValue<
T extends ObjectValueCollectorTypes,
V = string,
Expand Down Expand Up @@ -544,6 +538,51 @@ export type UnknownCollector = {
* @interface AutoCollector - Represents a collector that collects a value programmatically without user intervention.
*/

export interface ProtectOutputValue {
behavioralDataCollection: boolean;
universalDeviceIdentification: boolean;
}

export interface AttestationValue
extends Omit<PublicKeyCredential, 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON'> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So are we omitting them because we want a complete override below? it seems odd to omit them but then redefine many of the same fields?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I wasn't sure what the best way was to do this. This was the only way I could get the properties to override. Open to other suggestions.

rawId: string;
response: {
// https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAttestationResponse
clientDataJSON: string;
attestationObject: string;
};
}
export interface FidoRegistrationInputValue {
attestationValue?: AttestationValue;
}

export interface FidoRegistrationOutputValue {
publicKeyCredentialCreationOptions: FidoRegistrationOptions;
action: 'REGISTER';
trigger: string;
}

export interface AssertionValue
extends Omit<PublicKeyCredential, 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON'> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose same question, maybe i'm not understanding something regarding this

rawId: string;
response: {
// https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse
clientDataJSON: string;
authenticatorData: string;
signature: string;
userHandle: string | null;
};
}
export interface FidoAuthenticationInputValue {
assertionValue?: AssertionValue;
}

export interface FidoAuthenticationOutputValue {
publicKeyCredentialRequestOptions: FidoAuthenticationOptions;
action: 'AUTHENTICATE';
trigger: string;
}

export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector';
export type SingleValueAutoCollectorTypes = 'SingleValueAutoCollector' | 'ProtectCollector';
export type ObjectValueAutoCollectorTypes =
Expand All @@ -556,6 +595,7 @@ export interface AutoCollector<
C extends AutoCollectorCategories,
T extends AutoCollectorTypes,
IV = string,
OV = Record<string, unknown>,
> {
category: C;
error: string | null;
Expand All @@ -571,24 +611,27 @@ export interface AutoCollector<
output: {
key: string;
type: string;
config: Record<string, unknown>;
config: OV;
};
}

export type ProtectCollector = AutoCollector<
'SingleValueAutoCollector',
'ProtectCollector',
string
string,
ProtectOutputValue
>;
export type FidoRegistrationCollector = AutoCollector<
'ObjectValueAutoCollector',
'FidoRegistrationCollector',
FidoRegistrationInputValue
FidoRegistrationInputValue,
FidoRegistrationOutputValue
>;
export type FidoAuthenticationCollector = AutoCollector<
'ObjectValueAutoCollector',
'FidoAuthenticationCollector',
FidoAuthenticationInputValue
FidoAuthenticationInputValue,
FidoAuthenticationOutputValue
>;
export type SingleValueAutoCollector = AutoCollector<
'SingleValueAutoCollector',
Expand Down
14 changes: 13 additions & 1 deletion packages/davinci-client/src/lib/davinci.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,25 @@ export type ProtectField = {
};

export interface FidoRegistrationOptions
extends Omit<PublicKeyCredentialCreationOptions, 'challenge' | 'user'> {
extends Omit<
PublicKeyCredentialCreationOptions,
'challenge' | 'user' | 'pubKeyCredParams' | 'excludeCredentials'
> {
challenge: number[];
user: {
id: number[];
name: string;
displayName: string;
};
pubKeyCredParams: {
alg: string | number;
type: PublicKeyCredentialType;
}[];
excludeCredentials?: {
id: number[];
transports?: AuthenticatorTransport[];
type: PublicKeyCredentialType;
}[];
}

export type FidoRegistrationField = {
Expand Down
3 changes: 2 additions & 1 deletion packages/davinci-client/src/lib/davinci.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export function transformSubmitRequest(
collector.category === 'SingleValueCollector' ||
collector.category === 'ValidatedSingleValueCollector' ||
collector.category === 'ObjectValueCollector' ||
collector.category === 'SingleValueAutoCollector',
collector.category === 'SingleValueAutoCollector' ||
collector.category === 'ObjectValueAutoCollector',
);

const formData = collectors?.reduce<{
Expand Down
70 changes: 70 additions & 0 deletions packages/davinci-client/src/lib/fido/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# FIDO Module for DaVinci

## Overview

The `fido` API provides an interface for registering and authenticating with the WebAuthn API and transforming data to and from DaVinci. These methods transform options from DaVinci into WebAuthn compatible options, then call `navigator.credentials.create` or `navigator.credentials.get`, and finally transform the output of the WebAuthn API into a valid payload to send back to DaVinci.

## Installation and Initialization

The `fido` module is exported as a member of the `@forgerock/davinci-client` package and is intended to be used alongside the `davinciClient` to progress through a flow. To install the necessary dependencies, run:

```bash
npm install @forgerock/davinci-client --save
```

After installing, import and initialize the clients:

```typescript
import { davinci, fido } from '@forgerock/davinci-client';

const davinciClient = await davinci({ config });
const fidoApi = fido();
```

## API methods

### Registration

**register(options: FidoRegistrationOptions) => Promise<FidoRegistrationInputValue | GenericError>**

Creates a keypair and returns a public key credential formatted for DaVinci or an error

### Authentication

**authenticate: (options: FidoAuthenticationOptions) => Promise<FidoAuthenticationInputValue | GenericError>**

Creates an assertion to send to DaVinci for authentication

## Example Usage

### Registration Example

```typescript
if (collector.type === 'FidoRegistrationCollector') {
const credentialOptions = collector.output.config.publicKeyCredentialCreationOptions;
const publicKeyCredential = await fidoApi.register(credentialOptions);
if ('error' in publicKeyCredential) {
// Handle error
} else {
// Update the FidoRegistrationCollector with the credential
const updater = davinciClient.update(collector);
updater(publicKeyCredential);
}
}
```

### Authentication Example

```typescript
if (collector.type === 'FidoAuthenticationCollector') {
const credentialOptions = collector.output.config.publicKeyCredentialRequestOptions;
const assertion = await fidoApi.authenticate(credentialOptions);
if ('error' in assertion) {
// Handle error
} else {
// Update the FidoAuthenticationCollector with the credential
const updater = davinciClient.update(collector);
updater(assertion);
}
}
```
Loading