Skip to content

Commit

Permalink
feat: openid-federation models
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht committed Jul 9, 2024
1 parent c8d26f9 commit 9b9b6df
Show file tree
Hide file tree
Showing 101 changed files with 1,838 additions and 564 deletions.
11 changes: 5 additions & 6 deletions .github/workflows/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,16 @@ jobs:
run: pnpm install

- name: Check formatting
run: pnpm check-format:ci
run: pnpm check:style

- name: Build
run: pnpm build

- name: Check types
run: pnpm check-types
run: pnpm check:types

# TODO: In pnpm the tests are failing when there is not a node_modules present in the package. Haven't found a fix for it yet.
# - name: Test
# run: pnpm test
- name: Test core package
run: pnpm -F=core test

continious-deployment:
if: github.event_name == 'workflow_dispatch'
Expand Down Expand Up @@ -100,4 +99,4 @@ jobs:
prerelease: ${{ inputs.release-type == 'alpha' }}
token: ${{ secrets.GH_TOKEN }}
tag_name: v${{ steps.publish.outputs.LERNA_VERSION }}
name: Release v${{ steps.publish.outputs.LERNA_VERSION }}
name: Release v${{ steps.publish.outputs.LERNA_VERSION }}
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

153 changes: 153 additions & 0 deletions docs/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# OpenID Federation - Draft 36 - Design Document

Design document for OpenID Federation Draft 36 for the initial implementation where the client resolution is the main focus.

This library will allow the following (in the first 0.1.0 version):

1. Resolve a trust chain
1. Fetch entity statements
2. Resolve entity metadata
3. Fetch subordinate listings
2. Build a Federation Configuration
3. Fetch endpoint functionality based on the configuration
4. Subordinate listing endpoint functionality based on the configuration

## Terms

### Entity Statement

A signed JWT that contains the information needed for an Entity to partificate in federation(s), including metadata about itself and policies that apply to other Entities that it is authoritative for.

### Subordiate Statement

Entity statement issues by a Superior Entity about the Entity that is its Immediate Subordinate.

### Entity configuration

self issued Entity Statement (typivally a leaf entity)

## Questions

- How do we want to identify the trust anchors we allow?
- Subjects within a specific federation ecosystem?
- How do we allow users to define a federation ecosystem?

### Interfaces

#### Entity Statement

```typescript
type JsonWebKeySet = never; // TBD
type JsonWebKey = never; // TBD
type JsonWebToken = never; // TBD
type JsonObject<T = unknown> = Record<string | number, T>;

enum EntityTypeIdentifier {
FederationEntity = "federation_entity",
OpenIdRelyingParty = "openid_relying_party",
OpenIdProvider = "openid_provider",
}

type EntityStatement<
Metadata extends JsonObject = JsonObject,
MetadataPolicy extends JsonObject = JsonObject,
Constraints extends JsonObject = JsonObject,
> = {
iss: string;
sub: string;
iat: number;
exp: number;
jwks: JsonWebKeySet;
authorityHints?: Array<string>;
metadata?: Metadata;
metadataPolicy?: MetadataPolicy;
constraints?: Constraints;
crit?: Array<keyof EntityStatement<Metadata, MetadataPolicy, Constraints>>;
metadataPolicyCrit?: Array<keyof MetadataPolicy>;
trustMarks?: Array<{
id: string;
trust_mark: JsonWebToken;
}>;
trustMarkIssuers?: Record<string, Array<string>>;
trustMarkOwners?: Record<
string,
{ sub: string; jwks: JsonWebKeySet } & Record<string, unknown>
>;
sourceEndpoint?: string;
};

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };

type LeafAndIntermediateEntityConfiguration<
Metadata extends JsonObject = JsonObject,
MetadataPolicy extends JsonObject = JsonObject,
Constraints extends JsonObject = JsonObject,
> = Omit<
WithRequired<
EntityStatement<Metadata, MetadataPolicy, Constraints>,
"authorityHints"
>,
"trustMarkIssuers" | "trustMarkOwners"
>;

type TrustAnchorEntityConfiguration = EntityStatement;

type TrustChain =
| [TrustAnchorEntityConfiguration]
| [
LeafAndIntermediateEntityConfiguration,
...Array<LeafAndIntermediateEntityConfiguration>,
TrustAnchorEntityConfiguration,
];
```

```typescript
type ResolveTrustChain = (options: {
leafEntityConfiguration: EntityConfiguration;
trustAnchorSub: string; // TODO: is this how we want to manage trust anchors?
}) => Promise<TrustChain>;
```

```typescript
type FetchEntityStatement = ({
iss,
sub = iss,
}: {
iss: string;
sub?: string;
}) => Promise<EntityStatement>;
```

```typescript
// Initial implementation will return 400 + unsupported_parameter for ALL parameters
type RequestSubordinateListing = (options: {
entityType?: EntityTypeIdentifier; // TODO: confirm type
trustMarked?: boolean;
trustMarkId?: string; // TODO: confirm type
intermediate?: boolean;
}) => Promise<Array<string>>;
```

```typescript
type ResolveEntityStatement = (chache?: Cache) => (options: {
sub: string;
anchor: string;
type?: EntityTypeIdentifier;
}) => Promise<{
iss: string;
sub: string;
exp: number;
iat: number;
metadata: JsonObject;
trustMarks?: Array<JsonObject>;
trustChain?: Array<JsonObject>; // TODO: confirm type
}>;
```

```typescript
const resolve = () => () => {};

const cachedResolve = resolve(chain);

cachedResolve();
```
50 changes: 50 additions & 0 deletions docs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
type JsonWebKeySet = never // TBD
type JsonWebKey = never // TBD
type JsonWebToken = never // TBD
type JsonObject<T = unknown> = Record<string | number, T>

type EntityStatement<
Metadata extends JsonObject = JsonObject,
MetadataPolicy extends JsonObject = JsonObject,
Constraints extends JsonObject = JsonObject,
> = {
iss: string
sub: string
iat: number
exp: number
jwks: JsonWebKeySet
authorityHints?: Array<string>
metadata?: Metadata
metadataPolicy?: MetadataPolicy
constraints?: Constraints
crit?: Array<keyof EntityStatement<Metadata, MetadataPolicy, Constraints>>
metadataPolicyCrit?: Array<keyof MetadataPolicy>
trustMarks?: Array<{
id: string
trust_mark: JsonWebToken
}>
trustMarkIssuers?: Record<string, Array<string>>
trustMarkOwners?: Record<string, { sub: string; jwks: JsonWebKeySet } & Record<string, unknown>>
sourceEndpoint?: string
}

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }

type LeafAndIntermediateEntityConfiguration<
Metadata extends JsonObject = JsonObject,
MetadataPolicy extends JsonObject = JsonObject,
Constraints extends JsonObject = JsonObject,
> = Omit<
WithRequired<EntityStatement<Metadata, MetadataPolicy, Constraints>, 'authorityHints'>,
'trustMarkIssuers' | 'trustMarkOwners'
>

type TrustAnchorEntityConfiguration = EntityStatement

type TrustChain =
| [TrustAnchorEntityConfiguration]
| [
LeafAndIntermediateEntityConfiguration,
...Array<LeafAndIntermediateEntityConfiguration>,
TrustAnchorEntityConfiguration,
]
16 changes: 6 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,28 @@
"name": "openid-federation-ts",
"private": true,
"version": "0.1.0",
"description": "",
"main": "src/index.ts",
"keywords": [],
"description": "Openid Federation implementation",
"author": "Animo Solutions",
"license": "Apache-2.0",
"scripts": {
"clean": "rimraf **/build",
"clean:deps ": "pnpm clean && rimraf node_modules",
"build": "pnpm -r build",
"check-format": "pnpm biome check .",
"check-format:ci": "pnpm biome ci .",
"check-types": "pnpm build --noEmit",
"format": "pnpm check-format --write",
"check:style": "pnpm biome check .",
"check:types": "pnpm build --noEmit",
"format": "pnpm check:style --write --unsafe",
"test": "pnpm -r test",
"release": "lerna publish"
},
"devDependencies": {
"@biomejs/biome": "1.8.0",
"rimraf": "^5.0.7",
"tsx": "^4.13.1"
"rimraf": "^5.0.7"
},
"pnpm": {
"overrides": {
"typescript": "~5.3.2",
"@types/node": "^20.11.1",
"tsx": "^4.13.1"
"ts-node": "^10.9.2"
}
}
}
8 changes: 8 additions & 0 deletions packages/core/__tests__/fixtures/constraintsFigure17.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const constraintsFigure17 = {
max_path_length: 2,
naming_constraints: {
permitted: ['https://.example.com'],
excluded: ['https://east.example.com'],
},
allowed_entity_types: ['openid_provider', 'openid_relying_party'],
}
23 changes: 23 additions & 0 deletions packages/core/__tests__/fixtures/entityConfigurationFigure18.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const entityConfigurationFigure18 = {
iss: 'https://rp.example.it/spid/',
sub: 'https://rp.example.it/spid/',
iat: 1516239022,
exp: 1516298022,
// Added property as the example in the spec is invalid
jwks: { keys: [] },
trust_marks: [
{
id: 'https://www.spid.gov.it/certification/rp',
trust_mark:
'eyJraWQiOiJmdWtDdUtTS3hwWWJjN09lZUk3Ynlya3N5a0E1bDhPb2RFSXVyOHJoNFlBIiwidHlwIjoidHJ1c3QtbWFyaytqd3QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3d3dy5hZ2lkLmdvdi5pdCIsInN1YiI6Imh0dHBzOi8vcnAuZXhhbXBsZS5pdC9zcGlkIiwiaWF0IjoxNTc5NjIxMTYwLCJpZCI6Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAiLCJsb2dvX3VyaSI6Imh0dHBzOi8vd3d3LmFnaWQuZ292Lml0L3RoZW1lcy9jdXN0b20vYWdpZC9sb2dvLnN2ZyIsInJlZiI6Imh0dHBzOi8vZG9jcy5pdGFsaWEuaXQvZG9jcy9zcGlkLWNpZS1vaWRjLWRvY3MvaXQvdmVyc2lvbmUtY29ycmVudGUvIn0.AGf5Y4MoJt22rznH4i7Wqpb2EF2LzE6BFEkTzY1dCBMCK-8P_vj4Boz7335pUF45XXr2jx5_waDRgDoS5vOO-wfc0NWb4Zb_T1RCwcryrzV0z3jJICePMPM_1hZnBZjTNQd4EsFNvKmUo_teR2yzAZjguR2Rid30O5PO8kJtGaXDmz-rWaHbmfLhlNGJnqcp9Lo1bhkU_4Cjpn2bdX7RN0JyfHVY5IJXwdxUMENxZd-VtA5QYiw7kPExT53XcJO89ebe_ik4D0dl-vINwYhrIz2RPnqgA1OdbK7jg0vm8Tb3aemRLG7oLntHwqLO-gGYr6evM2_SgqwA0lQ9mB9yhw',
},
],
metadata: {
openid_relying_party: {
application_type: 'web',
client_registration_types: ['automatic'],
client_name: 'https://rp.example.it/spid/',
contacts: ['[email protected]'],
},
},
}
42 changes: 42 additions & 0 deletions packages/core/__tests__/fixtures/entityConfigurationFigure43.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export const entityConfigurationFigure43 = {
iss: 'https://openid.sunet.se',
sub: 'https://openid.sunet.se',
iat: 1516239022,
exp: 1516298022,
metadata: {
federation_entity: {
contacts: ['[email protected]'],
federation_fetch_endpoint: 'https://sunet.se/openid/fedapi',
homepage_uri: 'https://www.sunet.se',
organization_name: 'SUNET',
},
openid_provider: {
issuer: 'https://openid.sunet.se',
signed_jwks_uri: 'https://openid.sunet.se/jwks.jose',
authorization_endpoint: 'https://openid.sunet.se/authorization',
client_registration_types_supported: ['automatic', 'explicit'],
grant_types_supported: ['authorization_code'],
id_token_signing_alg_values_supported: ['ES256', 'RS256'],
logo_uri: 'https://www.umu.se/img/umu-logo-left-neg-SE.svg',
op_policy_uri: 'https://www.umu.se/en/website/legal-information/',
response_types_supported: ['code'],
subject_types_supported: ['pairwise', 'public'],
token_endpoint: 'https://openid.sunet.se/token',
federation_registration_endpoint: 'https://op.umu.se/openid/fedreg',
token_endpoint_auth_methods_supported: ['private_key_jwt'],
},
},
jwks: {
keys: [
{
alg: 'RS256',
e: 'AQAB',
kid: 'key1',
kty: 'RSA',
n: 'pnXBOusEANuug6ewezb9J_...',
use: 'sig',
},
],
},
authority_hints: ['https://edugain.org/federation'],
}
40 changes: 40 additions & 0 deletions packages/core/__tests__/fixtures/entityConfigurationFigure50.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const entityConfigurationFigure50 = {
authority_hints: ['https://umu.se'],
exp: 1568397247,
iat: 1568310847,
iss: 'https://op.umu.se',
sub: 'https://op.umu.se',
jwks: {
keys: [
{
e: 'AQAB',
kid: 'dEEtRjlzY3djcENuT01wOGxrZlkxb3RIQVJlMTY0...',
kty: 'RSA',
n: 'x97YKqc9Cs-DNtFrQ7_vhXoH9bwkDWW6En2jJ044yH...',
},
],
},
metadata: {
openid_provider: {
issuer: 'https://op.umu.se/openid',
signed_jwks_uri: 'https://op.umu.se/openid/jwks.jose',
authorization_endpoint: 'https://op.umu.se/openid/authorization',
client_registration_types_supported: ['automatic', 'explicit'],
request_parameter_supported: true,
grant_types_supported: ['authorization_code', 'implicit', 'urn:ietf:params:oauth:grant-type:jwt-bearer'],
id_token_signing_alg_values_supported: ['ES256', 'RS256'],
logo_uri: 'https://www.umu.se/img/umu-logo-left-neg-SE.svg',
op_policy_uri: 'https://www.umu.se/en/website/legal-information/',
response_types_supported: ['code', 'code id_token', 'token'],
subject_types_supported: ['pairwise', 'public'],
token_endpoint: 'https://op.umu.se/openid/token',
federation_registration_endpoint: 'https://op.umu.se/openid/fedreg',
token_endpoint_auth_methods_supported: [
'client_secret_post',
'client_secret_basic',
'client_secret_jwt',
'private_key_jwt',
],
},
},
}
Loading

0 comments on commit 9b9b6df

Please sign in to comment.