Skip to content

Commit

Permalink
feat: Add support for openIdConnect security scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
Embraser01 committed Sep 9, 2024
1 parent dcce966 commit 927ed64
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 12 deletions.
7 changes: 7 additions & 0 deletions .yarn/versions/f2947d6d.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
releases:
"@typoas/cli": minor
"@typoas/generator": minor
"@typoas/runtime": minor

declined:
- "@typoas/react-query"
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add support for `openIdConnect` security scheme type (fixes [#69](https://github.com/Embraser01/typoas/pull/69)) [#70](https://github.com/Embraser01/typoas/pull/70)

---

## 4.0.0 - 2024-08-21
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ Main features are:
- **React Query** integration
- Support for `allOf`, `oneOf` and `anyOf` schemas.
- Automatically convert `format: 'date-time'` to JS `Date`
- Handle **API Key**, **HTTP Config** and **OAuth2**<sup>1</sup> auth security schemes
- Handle **API Key**, **HTTP Config**, **OAuth2**<sup>1</sup> and **OIDC**<sup>1</sup> auth security schemes
- JSDoc for schemas and operations
- Uses `fetch` api (can be customized)
- Non JSON content type support
- Small bundle size
- And more...

> <sup>1</sup>: OAuth2 scheme does not handle flows to retrieve an `accessToken`.
> <sup>1</sup>: OAuth2 and OpenIDConnect scheme do not handle flows to retrieve an `accessToken`.
> You need to provide your own `accessToken` through the `provider.getConfig()` function.
The project is split into 4 packages:
Expand Down Expand Up @@ -253,11 +253,12 @@ const ctx = createContext({
});
```

It supports 4 types of security schemes:
It supports 5 types of security schemes:

- `apiKey` mode
- `http` bearer and basic mode
- `oauth2` mode
- `openIdConnect` mode

The `getConfig` function should return the configuration needed to authenticate the request. Returning `null` will skip the authentification.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export function createConfigTypeFromSecurityScheme(
case 'oauth2':
return createRuntimeRefType(ExportedRef.OAuth2SecurityAuthentication);
case 'openIdConnect':
return createRuntimeRefType(ExportedRef.OAuth2SecurityAuthentication);
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Unsupported security scheme '${securityScheme.type}'`);
}
}
Expand Down Expand Up @@ -123,6 +126,15 @@ export function createRuntimeSecurityClass(
[factory.createObjectLiteralExpression(args), authProviderExpression],
);
case 'openIdConnect':
return factory.createNewExpression(
createRuntimeRefProperty(
ExportedRef.OpenIdConnectSecurityAuthentication,
),
undefined,
[factory.createObjectLiteralExpression(args), authProviderExpression],
);
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Unsupported security scheme '${securityScheme.type}'`);
}
}
1 change: 1 addition & 0 deletions packages/typoas-generator/src/generator/utils/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum ExportedRef {
HttpBasicSecurityAuthentication = 'HttpBasicSecurityAuthentication',
HttpBearerSecurityAuthentication = 'HttpBearerSecurityAuthentication',
OAuth2SecurityAuthentication = 'OAuth2SecurityAuthentication',
OpenIdConnectSecurityAuthentication = 'OpenIdConnectSecurityAuthentication',
StatusResponse = 'StatusResponse',
BaseFetcherData = 'BaseFetcherData',
}
Expand Down
9 changes: 9 additions & 0 deletions packages/typoas-runtime/src/auth/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@ export interface SecurityAuthentication {
export interface AuthProvider<T> {
getConfig(): Promise<T | null> | T | null;
}

/**
* Configuration for the OAuth2 and OIDC authentication scheme.
*/
export type BaseFlowConfig = {
accessToken: string;
// Allow overriding the token type (default: Bearer)
tokenType?: string;
};
1 change: 1 addition & 0 deletions packages/typoas-runtime/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './api-key-auth';
export * from './http-auth-basic';
export * from './http-auth-bearer';
export * from './oauth2-auth';
export * from './open-id-connect-auth';
12 changes: 5 additions & 7 deletions packages/typoas-runtime/src/auth/oauth2-auth.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import type { AuthProvider, SecurityAuthentication } from './base';
import type {
AuthProvider,
BaseFlowConfig,
SecurityAuthentication,
} from './base';
import type { RequestContext } from '../fetcher';

// We don't actually need to have any configuration for OAuth2
// because we only use the already generated accessToken.
export type OAuth2Configuration = Record<string, never>;

export type BaseFlowConfig = {
accessToken: string;
// Allow overriding the token type (default: Bearer)
tokenType?: string;
};

export class OAuth2SecurityAuthentication implements SecurityAuthentication {
constructor(
private config: OAuth2Configuration,
Expand Down
34 changes: 34 additions & 0 deletions packages/typoas-runtime/src/auth/open-id-connect-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type {
AuthProvider,
BaseFlowConfig,
SecurityAuthentication,
} from './base';
import type { RequestContext } from '../fetcher';

// We don't actually need to have any configuration for OpenIdConnect
// because we only use the already generated accessToken.
export type OpenIdConnectConfiguration = Record<string, never>;

export class OpenIdConnectSecurityAuthentication
implements SecurityAuthentication
{
constructor(
private config: OpenIdConnectConfiguration,
private provider?: AuthProvider<BaseFlowConfig>,
) {}

async applySecurityAuthentication(context: RequestContext): Promise<void> {
if (!this.provider) return;

const res = await this.provider.getConfig();
if (res === null) {
return;
}

const { accessToken, tokenType } = res;
return context.setHeaderParam(
'Authorization',
`${tokenType || 'Bearer'} ${accessToken}`,
);
}
}
10 changes: 8 additions & 2 deletions packages/typoas-runtime/src/context/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
SerializerOptions,
} from '../fetcher';
import type { TransformEntity } from '../transformers';
import type { AuthProvider, SecurityAuthentication } from '../auth';
import {
AuthProvider,
OpenIdConnectSecurityAuthentication,
SecurityAuthentication,
} from '../auth';
import type { Transform } from '../transformers';
import type { BaseServerConfiguration } from '../configuration';
import type {
Expand Down Expand Up @@ -89,7 +93,9 @@ export type AuthProviderConfig<T extends SecurityAuthentication> =
? BearerAuthConfig
: T extends OAuth2SecurityAuthentication
? BaseFlowConfig
: never;
: T extends OpenIdConnectSecurityAuthentication
? BaseFlowConfig
: never;

export type ResponseHandler = {
transforms?: TransformEntity;
Expand Down

0 comments on commit 927ed64

Please sign in to comment.