@epsbv/oauth-sdk 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env ADDED
@@ -0,0 +1,4 @@
1
+ NODE_ENV="production"
2
+
3
+ IDENTITY_DNS="http://localhost:7550"
4
+ OAUTH_DNS="https://localhost:7560"
@@ -0,0 +1,10 @@
1
+ import { ClientOptions } from "./types";
2
+ import { FetchMethod, FetchOptions } from "./utils/fetch";
3
+ export declare class ApiClient {
4
+ private baseUrl;
5
+ private token;
6
+ protected constructor({ token, baseUrl }: ClientOptions);
7
+ protected fetchUrlEncoded<T = never>(endpoint: string, method: FetchMethod, params?: FetchOptions): Promise<T>;
8
+ protected fetchMultipart<T = never>(endpoint: string, method: FetchMethod, params?: FetchOptions): Promise<T>;
9
+ protected get options(): ClientOptions;
10
+ }
@@ -0,0 +1,24 @@
1
+ import { fetchMultipart, fetchUrlEncoded } from "./utils/fetch";
2
+ export class ApiClient {
3
+ baseUrl;
4
+ token;
5
+ constructor({ token, baseUrl }) {
6
+ this.token = token;
7
+ this.baseUrl = baseUrl;
8
+ }
9
+ async fetchUrlEncoded(endpoint, method, params) {
10
+ const headers = new Headers(params?.headers ?? {});
11
+ headers.append('User-Agent', 'eps/sdk-client');
12
+ headers.append('Authorization', `Bearer ${this.token}`);
13
+ return await fetchUrlEncoded(`${this.baseUrl}${endpoint}`, method, { body: params?.body ?? null, headers });
14
+ }
15
+ async fetchMultipart(endpoint, method, params) {
16
+ const headers = new Headers(params?.headers ?? {});
17
+ headers.append('User-Agent', 'eps/sdk-client');
18
+ headers.append('Authorization', `Bearer ${this.token}`);
19
+ return await fetchMultipart(`${this.baseUrl}${endpoint}`, method, { body: params?.body ?? null, headers });
20
+ }
21
+ get options() {
22
+ return { token: this.token, baseUrl: this.baseUrl };
23
+ }
24
+ }
@@ -0,0 +1,2 @@
1
+ export declare const apiUrl = "https://identity.eps-connect.eu";
2
+ export declare const oauthUrl = "https://oauth.eps-connect.eu";
package/build/const.js ADDED
@@ -0,0 +1,3 @@
1
+ // This file is auto-generated. Do not edit manually.
2
+ export const apiUrl = "https://identity.eps-connect.eu";
3
+ export const oauthUrl = "https://oauth.eps-connect.eu";
@@ -0,0 +1,20 @@
1
+ export declare class FetchError extends Error {
2
+ constructor(cause: unknown);
3
+ }
4
+ export declare class RequestError extends Error {
5
+ code: string;
6
+ description: string | null;
7
+ uri: string | null;
8
+ state: string | null;
9
+ constructor(code: string, description: string | null, uri: string | null, state: string | null);
10
+ static create(result: object): RequestError;
11
+ }
12
+ export declare class UnexpectedResponseError extends Error {
13
+ status: number;
14
+ constructor(responseStatus: number);
15
+ }
16
+ export declare class UnexpectedErrorResponseBodyError extends Error {
17
+ status: number;
18
+ data: unknown;
19
+ constructor(status: number, data: unknown);
20
+ }
package/build/error.js ADDED
@@ -0,0 +1,67 @@
1
+ export class FetchError extends Error {
2
+ constructor(cause) {
3
+ super("Failed to send request", {
4
+ cause
5
+ });
6
+ }
7
+ }
8
+ export class RequestError extends Error {
9
+ code;
10
+ description;
11
+ uri;
12
+ state;
13
+ constructor(code, description, uri, state) {
14
+ super(`OAuth request error: ${code}`);
15
+ this.code = code;
16
+ this.description = description;
17
+ this.uri = uri;
18
+ this.state = state;
19
+ }
20
+ static create(result) {
21
+ let code;
22
+ if ("error" in result && typeof result.error === "string") {
23
+ code = result.error;
24
+ }
25
+ else {
26
+ throw new Error("Invalid error response");
27
+ }
28
+ let description = null;
29
+ let uri = null;
30
+ let state = null;
31
+ if ("error_description" in result) {
32
+ if (typeof result.error_description !== "string") {
33
+ throw new Error("Invalid data");
34
+ }
35
+ description = result.error_description;
36
+ }
37
+ if ("error_uri" in result) {
38
+ if (typeof result.error_uri !== "string") {
39
+ throw new Error("Invalid data");
40
+ }
41
+ uri = result.error_uri;
42
+ }
43
+ if ("state" in result) {
44
+ if (typeof result.state !== "string") {
45
+ throw new Error("Invalid data");
46
+ }
47
+ state = result.state;
48
+ }
49
+ return new RequestError(code, description, uri, state);
50
+ }
51
+ }
52
+ export class UnexpectedResponseError extends Error {
53
+ status;
54
+ constructor(responseStatus) {
55
+ super("Unexpected error response");
56
+ this.status = responseStatus;
57
+ }
58
+ }
59
+ export class UnexpectedErrorResponseBodyError extends Error {
60
+ status;
61
+ data;
62
+ constructor(status, data) {
63
+ super("Unexpected error response body");
64
+ this.status = status;
65
+ this.data = data;
66
+ }
67
+ }
@@ -0,0 +1,35 @@
1
+ import { Device, Identity } from "./types";
2
+ import { ApiClient } from "../client";
3
+ import { ClientOptions, Language } from "../types";
4
+ export * from './types';
5
+ export declare class IdentityClient extends ApiClient {
6
+ constructor({ token }: Omit<ClientOptions, 'baseUrl'>);
7
+ get info(): Identity$Info;
8
+ get avatar(): Identity$Avatar;
9
+ get devices(): Identity$Devices;
10
+ }
11
+ declare class Identity$Info extends ApiClient {
12
+ constructor(options: ClientOptions);
13
+ get(): Promise<Identity>;
14
+ delete(): Promise<never>;
15
+ update(body: {
16
+ first_name: string;
17
+ last_name: string;
18
+ language: Language;
19
+ }): Promise<{}>;
20
+ }
21
+ declare class Identity$Avatar extends ApiClient {
22
+ constructor(options: ClientOptions);
23
+ get(): Promise<string>;
24
+ delete(): Promise<never>;
25
+ update(body: {
26
+ avatar: File;
27
+ }): Promise<{}>;
28
+ }
29
+ declare class Identity$Devices extends ApiClient {
30
+ constructor(options: ClientOptions);
31
+ get(): Promise<Device[]>;
32
+ delete({ id }: {
33
+ id: string;
34
+ }): Promise<never>;
35
+ }
@@ -0,0 +1,56 @@
1
+ import { ApiClient } from "../client";
2
+ import { apiUrl } from "../const";
3
+ export * from './types';
4
+ export class IdentityClient extends ApiClient {
5
+ constructor({ token }) {
6
+ super({ token, baseUrl: apiUrl });
7
+ }
8
+ get info() {
9
+ return new Identity$Info(this.options);
10
+ }
11
+ get avatar() {
12
+ return new Identity$Avatar(this.options);
13
+ }
14
+ get devices() {
15
+ return new Identity$Devices(this.options);
16
+ }
17
+ }
18
+ class Identity$Info extends ApiClient {
19
+ constructor(options) {
20
+ super(options);
21
+ }
22
+ async get() {
23
+ return await this.fetchUrlEncoded('/e/identity', 'GET');
24
+ }
25
+ async delete() {
26
+ return await this.fetchUrlEncoded('/e/identity', 'DELETE');
27
+ }
28
+ async update(body) {
29
+ return await this.fetchUrlEncoded('/e/identity', 'PUT', { body });
30
+ }
31
+ }
32
+ class Identity$Avatar extends ApiClient {
33
+ constructor(options) {
34
+ super(options);
35
+ }
36
+ async get() {
37
+ return await this.fetchUrlEncoded('/e/identity/avatar', 'GET');
38
+ }
39
+ async delete() {
40
+ return await this.fetchUrlEncoded('/e/identity/avatar', 'DELETE');
41
+ }
42
+ async update(body) {
43
+ return await this.fetchMultipart('/e/identity/avatar', 'PUT', { body });
44
+ }
45
+ }
46
+ class Identity$Devices extends ApiClient {
47
+ constructor(options) {
48
+ super(options);
49
+ }
50
+ async get() {
51
+ return await this.fetchUrlEncoded('/e/identity/devices', 'GET');
52
+ }
53
+ async delete({ id }) {
54
+ return await this.fetchUrlEncoded(`/e/identity/devices/${id}`, 'DELETE');
55
+ }
56
+ }
@@ -0,0 +1,22 @@
1
+ import { Language } from "../types";
2
+ export interface Identity {
3
+ uid: string;
4
+ admin: boolean;
5
+ email: string;
6
+ avatar?: string;
7
+ first_name: string;
8
+ last_name: string;
9
+ language: Language;
10
+ provider: 'google' | 'internal' | 'microsoft' | 'apple';
11
+ provider_id: string | undefined;
12
+ created_at: Date;
13
+ updated_at: Date;
14
+ verified_at?: Date;
15
+ }
16
+ export interface Device {
17
+ id: string;
18
+ name: string;
19
+ type: string;
20
+ created_at: Date;
21
+ activity_at: Date;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export * as oauth from './oauth';
2
+ export * as tenant from './tenant';
3
+ export * as identity from './identity';
4
+ export * from './types';
package/build/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * as oauth from './oauth';
2
+ export * as tenant from './tenant';
3
+ export * as identity from './identity';
4
+ export * from './types';
@@ -0,0 +1,15 @@
1
+ import { OAuthTokens, OAuthUrlOptions, type OAuthClientOptions } from "./types";
2
+ import { FetchMethod, FetchOptions } from "../utils/fetch";
3
+ export { generateState, generateCodeVerifier, createS256CodeChallenge } from '../utils/secret';
4
+ export declare class OAuthClient {
5
+ private clientId;
6
+ private redirectURI;
7
+ private credentials;
8
+ constructor({ clientId, clientSecret, redirectURI }: OAuthClientOptions);
9
+ createAuthorizationURL(options: OAuthUrlOptions): URL;
10
+ private createAuthorizationURLWithPKCE;
11
+ getToken(code: string, codeVerifier?: string): Promise<OAuthTokens>;
12
+ refreshAccessToken(refresh_token: string): Promise<OAuthTokens>;
13
+ revokeToken(token: string): Promise<void>;
14
+ fetch<T>(endpoint: string, method: FetchMethod, params?: FetchOptions): Promise<T>;
15
+ }
@@ -0,0 +1,105 @@
1
+ import { fetchUrlEncoded } from "../utils/fetch";
2
+ import { apiUrl, oauthUrl } from "../const";
3
+ import { encodeCredentials } from "../utils/secret";
4
+ export { generateState, generateCodeVerifier, createS256CodeChallenge } from '../utils/secret';
5
+ const authorizationEndpoint = `${oauthUrl}/oauth/auth`;
6
+ export class OAuthClient {
7
+ clientId;
8
+ redirectURI;
9
+ credentials;
10
+ constructor({ clientId, clientSecret, redirectURI }) {
11
+ this.clientId = clientId;
12
+ this.redirectURI = redirectURI;
13
+ this.credentials = encodeCredentials(clientId, clientSecret);
14
+ }
15
+ // Generate an authorization URL
16
+ createAuthorizationURL(options) {
17
+ return this.createAuthorizationURLWithPKCE(options);
18
+ }
19
+ createAuthorizationURLWithPKCE({ state, code_challenge_method, code_challenge, scope, response_type, access_type, include_granted_scopes, prompt }) {
20
+ const url = new URL(authorizationEndpoint);
21
+ url.searchParams.set('state', state);
22
+ url.searchParams.set('client_id', this.clientId);
23
+ url.searchParams.set('access_type', access_type ?? 'online'); // determines 'refresh' or 'access' token
24
+ url.searchParams.set('response_type', response_type); // determines if parameters returned are in fragment identifier or search params
25
+ url.searchParams.set('redirect_uri', this.redirectURI);
26
+ if (prompt) {
27
+ url.searchParams.set('prompt', prompt);
28
+ }
29
+ if (include_granted_scopes != undefined) {
30
+ url.searchParams.set('include_granted_scopes', `${include_granted_scopes}`);
31
+ }
32
+ if (code_challenge_method) {
33
+ url.searchParams.set('code_challenge_method', code_challenge_method);
34
+ if (code_challenge) {
35
+ url.searchParams.set('code_challenge', code_challenge);
36
+ }
37
+ }
38
+ if (scope.length > 0) {
39
+ url.searchParams.set('scope', scope.join(' '));
40
+ }
41
+ return url;
42
+ }
43
+ async getToken(code, codeVerifier) {
44
+ // Retreive the token from the OAuth server
45
+ try {
46
+ return await this.fetch('/e/oauth/token', 'POST', {
47
+ body: {
48
+ grant_type: 'authorization_code',
49
+ code,
50
+ redirect_uri: this.redirectURI,
51
+ code_verifier: codeVerifier
52
+ }
53
+ });
54
+ // const { header } = jwt.decode(token, { complete: true })
55
+ // const { keys } = await this.fetch<Jwks>('/oauth/jwks', 'GET')
56
+ // const certificate = keys[0];
57
+ // console.log(keys)
58
+ // if (certificate) {
59
+ // jwt.verify(
60
+ // token,
61
+ // `-----BEGIN PUBLIC KEY-----\n${certificate.x5c[0]}\n-----END PUBLIC KEY-----`, {
62
+ // algorithms: [certificate.alg]
63
+ // })
64
+ // }
65
+ }
66
+ catch (error) {
67
+ throw new Error(error);
68
+ }
69
+ }
70
+ async refreshAccessToken(refresh_token) {
71
+ try {
72
+ return await this.fetch('/e/oauth/token', 'POST', {
73
+ body: {
74
+ grant_type: 'refresh_token',
75
+ refresh_token: refresh_token,
76
+ }
77
+ });
78
+ // const { header } = jwt.decode(token, { complete: true })
79
+ // const { keys } = await this.fetch<Jwks>('/oauth/jwks', 'GET')
80
+ // const certificate = keys[0];
81
+ // console.log(keys)
82
+ // if (certificate) {
83
+ // jwt.verify(
84
+ // token,
85
+ // `-----BEGIN PUBLIC KEY-----\n${certificate.x5c[0]}\n-----END PUBLIC KEY-----`, {
86
+ // algorithms: [certificate.alg]
87
+ // })
88
+ // }
89
+ }
90
+ catch (error) {
91
+ throw new Error(error);
92
+ }
93
+ }
94
+ async revokeToken(token) {
95
+ await fetchUrlEncoded(`${apiUrl}/e/oauth/revoke`, 'POST', {
96
+ body: { token }
97
+ });
98
+ }
99
+ async fetch(endpoint, method, params) {
100
+ const headers = new Headers(params?.headers ?? {});
101
+ headers.append('User-Agent', 'eps/oauth-client');
102
+ headers.append('Authorization', `Bearer ${this.credentials}`);
103
+ return await fetchUrlEncoded(`${apiUrl}${endpoint}`, method, { body: params?.body ?? null, headers });
104
+ }
105
+ }
@@ -0,0 +1,108 @@
1
+ import { Algorithm } from "jsonwebtoken";
2
+ export type OAuthScope = 'email' | 'profile';
3
+ export interface OAuthClientOptions {
4
+ clientId: string;
5
+ clientSecret: string;
6
+ redirectURI: string;
7
+ }
8
+ export interface OAuthUrlOptions {
9
+ /**
10
+ * Recommended. Indicates whether your application can refresh access tokens
11
+ * when the user is not present at the browser. Valid parameter values are
12
+ * 'online', which is the default value, and 'offline'. Set the value to
13
+ * 'offline' if your application needs to refresh access tokens when the user
14
+ * is not present at the browser. This value instructs the Google
15
+ * authorization server to return a refresh token and an access token the
16
+ * first time that your application exchanges an authorization code for
17
+ * tokens.
18
+ */
19
+ access_type?: 'online' | 'offline';
20
+ /**
21
+ * Defaults back to 'code''.
22
+ */
23
+ response_type: 'code' | 'token';
24
+ /**
25
+ * Required. A space-delimited list of scopes that identify the resources that
26
+ * your application could access on the user's behalf. These values inform the
27
+ * consent screen that Google displays to the user. Scopes enable your
28
+ * application to only request access to the resources that it needs while
29
+ * also enabling users to control the amount of access that they grant to your
30
+ * application. Thus, there is an inverse relationship between the number of
31
+ * scopes requested and the likelihood of obtaining user consent. The
32
+ * OAuth 2.0 API Scopes document provides a full list of scopes that you might
33
+ * use to access Google APIs. We recommend that your application request
34
+ * access to authorization scopes in context whenever possible. By requesting
35
+ * access to user data in context, via incremental authorization, you help
36
+ * users to more easily understand why your application needs the access it is
37
+ * requesting.
38
+ */
39
+ scope?: OAuthScope[];
40
+ /**
41
+ * Recommended. Specifies any string value that your application uses to
42
+ * maintain state between your authorization request and the authorization
43
+ * server's response. The server returns the exact value that you send as a
44
+ * name=value pair in the hash (#) fragment of the 'redirect_uri' after the
45
+ * user consents to or denies your application's access request. You can use
46
+ * this parameter for several purposes, such as directing the user to the
47
+ * correct resource in your application, sending nonces, and mitigating
48
+ * cross-site request forgery. Since your redirect_uri can be guessed, using a
49
+ * state value can increase your assurance that an incoming connection is the
50
+ * result of an authentication request. If you generate a random string or
51
+ * encode the hash of a cookie or another value that captures the client's
52
+ * state, you can validate the response to additionally ensure that the
53
+ * request and response originated in the same browser, providing protection
54
+ * against attacks such as cross-site request forgery. See the OpenID Connect
55
+ * documentation for an example of how to create and confirm a state token.
56
+ */
57
+ state?: string;
58
+ /**
59
+ * Optional. Enables applications to use incremental authorization to request
60
+ * access to additional scopes in context. If you set this parameter's value
61
+ * to true and the authorization request is granted, then the new access token
62
+ * will also cover any scopes to which the user previously granted the
63
+ * application access. See the incremental authorization section for examples.
64
+ */
65
+ include_granted_scopes?: boolean;
66
+ /**
67
+ * Optional. A space-delimited, case-sensitive list of prompts to present the
68
+ * user. If you don't specify this parameter, the user will be prompted only
69
+ * the first time your app requests access. Possible values are:
70
+ *
71
+ * 'none' - Donot display any authentication or consent screens. Must not be
72
+ * specified with other values.
73
+ * 'consent' - Prompt the user for consent.
74
+ * 'select_account' - Prompt the user to select an account.
75
+ */
76
+ prompt?: 'consent' | 'select_account';
77
+ /**
78
+ * Recommended. Specifies what method was used to encode a 'code_verifier'
79
+ * that will be used during authorization code exchange. This parameter must
80
+ * be used with the 'code_challenge' parameter. The value of the
81
+ * 'code_challenge_method' defaults to "plain" if not present in the request
82
+ * that includes a 'code_challenge'. The only supported values for this
83
+ * parameter are "S256" or "plain".
84
+ */
85
+ code_challenge_method?: 'S256' | 'plain';
86
+ /**
87
+ * Recommended. Specifies an encoded 'code_verifier' that will be used as a
88
+ * server-side challenge during authorization code exchange. This parameter
89
+ * must be used with the 'code_challenge' parameter described above.
90
+ */
91
+ code_challenge?: string;
92
+ }
93
+ export interface OAuthTokens {
94
+ access_token: string;
95
+ refresh_token?: string;
96
+ }
97
+ export interface Jwks {
98
+ keys: Jwk[];
99
+ }
100
+ export interface Jwk {
101
+ alg: Algorithm;
102
+ e: string;
103
+ kid: string;
104
+ kty: string;
105
+ n: string;
106
+ use: string;
107
+ x5c: string[];
108
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,102 @@
1
+ import { ApiClient } from "../client";
2
+ import { ClientOptions, Language } from "../types";
3
+ import { Tenant, TenantRole, TenantUser } from "./types";
4
+ export * from './types';
5
+ export declare class TenantClient extends ApiClient {
6
+ constructor({ token }: Omit<ClientOptions, 'baseUrl'>);
7
+ get info(): Tenant$Info;
8
+ get avatar(): Tenant$Avatar;
9
+ get users(): Tenant$Users;
10
+ get partners(): Tenant$Partners;
11
+ }
12
+ declare class Tenant$Partners extends ApiClient {
13
+ constructor(options: ClientOptions);
14
+ get users(): Tenant$Partners$Users;
15
+ get avatar(): Tenant$Partners$Avatar;
16
+ list(): Promise<Tenant[]>;
17
+ get(params: {
18
+ id: string;
19
+ }): Promise<Tenant>;
20
+ update(params: {
21
+ id: string;
22
+ data: {
23
+ name: string;
24
+ email: string;
25
+ language: Language;
26
+ };
27
+ }): Promise<never>;
28
+ delete(params: {
29
+ id: string;
30
+ }): Promise<never>;
31
+ }
32
+ declare class Tenant$Partners$Avatar extends ApiClient {
33
+ constructor(options: ClientOptions);
34
+ get(params: {
35
+ id: string;
36
+ }): Promise<string>;
37
+ delete(params: {
38
+ id: string;
39
+ }): Promise<never>;
40
+ update(params: {
41
+ id: string;
42
+ data: {
43
+ avatar: File;
44
+ };
45
+ }): Promise<{}>;
46
+ }
47
+ declare class Tenant$Partners$Users extends ApiClient {
48
+ constructor(options: ClientOptions);
49
+ list(params: {
50
+ id: string;
51
+ }): Promise<TenantUser>;
52
+ get(params: {
53
+ id: string;
54
+ uid: string;
55
+ }): Promise<TenantUser>;
56
+ update(params: {
57
+ id: string;
58
+ uid: string;
59
+ data: {
60
+ first_name: string;
61
+ last_name: string;
62
+ role: TenantRole;
63
+ language: Language;
64
+ };
65
+ }): Promise<never>;
66
+ }
67
+ declare class Tenant$Users extends ApiClient {
68
+ constructor(options: ClientOptions);
69
+ list(): Promise<TenantUser>;
70
+ get(params: {
71
+ uid: string;
72
+ }): Promise<TenantUser>;
73
+ update(params: {
74
+ uid: string;
75
+ data: {
76
+ first_name: string;
77
+ last_name: string;
78
+ role: TenantRole;
79
+ language: Language;
80
+ };
81
+ }): Promise<never>;
82
+ delete(params: {
83
+ uid: string;
84
+ }): Promise<never>;
85
+ }
86
+ declare class Tenant$Info extends ApiClient {
87
+ constructor(options: ClientOptions);
88
+ get(): Promise<Tenant>;
89
+ update(data: {
90
+ name: string;
91
+ email: string;
92
+ language: Language;
93
+ }): Promise<void>;
94
+ }
95
+ declare class Tenant$Avatar extends ApiClient {
96
+ constructor(options: ClientOptions);
97
+ get(): Promise<string>;
98
+ delete(): Promise<never>;
99
+ update(body: {
100
+ avatar: File;
101
+ }): Promise<{}>;
102
+ }