Skip to content

Commit

Permalink
Feature: Config changes (#32)
Browse files Browse the repository at this point in the history
* changes configuration to implement frontendAPI and backendAPI

* fixes tests

* setting backend api in constructor

* resolves comments

* fixes test
  • Loading branch information
Dopeamin authored Sep 13, 2024
1 parent 38ebad4 commit 0fbc99d
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 45 deletions.
3 changes: 2 additions & 1 deletion .example.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
CORBADO_PROJECT_ID=pro-xxxxxxxxxxxxxxxxxxx
CORBADO_API_SECRET=corbado1_xxxxxxxxxxxxxxxxxxxxxxxxxx
CORBADO_BACKEND_API=https://backendapi.cloud.corbado.io/v2
CORBADO_BACKEND_API=https://backendapi.cloud.corbado.io
CORBADO_FRONTEND_API=https://[project-id].frontendapi.cloud.corbado.io
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The Corbado Node.js SDK provides the following services:
To use a specific service, such as `sessions`, invoke it as shown below:

```JavaScript
corbado.sessions().getAndValidateCurrentUser(req);
corbado.sessions().validateToken(req);
```

## :books: Advanced
Expand Down
14 changes: 7 additions & 7 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Configuration

The Corbado Node.js SDK uses a configuration object to manage various settings. This object is created using the `Config` class, which takes two parameters: `projectID` and `apiSecret`.
The Corbado Node.js SDK uses a configuration object to manage various settings. This object is created using the `Config` class, which takes 4 parameters: `projectID`, `apiSecret`, `frontendAPI` and `backendAPI`.

## Creating a Configuration Object

Expand All @@ -11,25 +11,25 @@ import { Config } from '@corbado/node-sdk';

const projectID = process.env.CORBADO_PROJECT_ID;
const apiSecret = process.env.CORBADO_API_SECRET;
const frontendAPI = process.env.CORBADO_FRONTEND_API;
const backendAPI = process.env.CORBADO_BACKEND_API;

const config = new Config(projectID, apiSecret);
const config = new Config(projectID, apiSecret, frontendAPI, backendAPI);
```

## Validation in Config Class

The `Config` class validates the `projectID` and `apiSecret` parameters. The `projectID` must start with 'pro-', and the `apiSecret` must start with 'corbado1'. If these conditions are not met, an error is thrown.
The `Config` class validates the `projectID`, `apiSecret`, `frontendAPI` and `backendAPI` parameters. The `projectID` must start with 'pro-', the `apiSecret` must start with 'corbado1', both API URLs should be valid domain names and the pathname should be empty. If these conditions are not met, an error is thrown.

## Config Class Properties

The `Config` class also sets several other properties:

- `BackendAPI`: The base URL for the backend API. By default, this is set to `https://backendapi.corbado.io`.
- `FrontendAPI`: The base URL for the frontend API. This is generated by replacing [projectID] in the default frontend API URL with the provided projectID.
- `ShortSessionCookieName`: The name of the short session cookie. By default, this is set to `cbo_short_session`.
- `CacheMaxAge`: The maximum age for the cache. By default, this is set to 60000 milliseconds (1 minute).
- `JWTIssuer`: The issuer for the JWT. This is generated by appending `/.well-known/jwks` to the FrontendAPI.
These properties are used throughout the SDK to configure various services. For example, the `BackendAPI`, `projectID`, and `apiSecret` are used to create an Axios client in the SDK class.

### nvironment Variables
### Environment Variables

Remember to set the `CORBADO_PROJECT_ID` and `CORBADO_API_SECRET` environment variables in your project. These are used to create the `Config` object.
Remember to set the `CORBADO_PROJECT_ID`, `CORBADO_API_SECRET`, `CORBADO_FRONTEND_API` and `CORBADO_BACKEND_API` environment variables in your project. These are used to create the `Config` object.
22 changes: 5 additions & 17 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export interface ConfigInterface {
}

export const DefaultClient = axios.create();
export const DefaultBackendAPI = 'https://backendapi.cloud.corbado.io/v2';
export const DefaultFrontendAPI = 'https://[projectID].frontendapi.cloud.corbado.io';
export const DefaultShortSessionCookieName = 'cbo_short_session';
export const DefaultCacheMaxAge = 10 * 60 * 1000; // 10 * 60 * 1000 = 60000 milliseconds, which is equivalent to 10 minutes.

Expand All @@ -24,34 +22,24 @@ class Config implements ConfigInterface {

FrontendAPI: string;

FrontendAPIWithCName: string;

BackendAPI: string = DefaultBackendAPI;
BackendAPI: string;

ShortSessionCookieName: string = DefaultShortSessionCookieName;

Client: AxiosInstance;

CacheMaxAge: number = DefaultCacheMaxAge;

constructor(projectID: string, apiSecret: string, cname?: string) {
constructor(projectID: string, apiSecret: string, frontendAPI: string, backendAPI: string) {
this.validateProjectID(projectID);
this.validateAPISecret(apiSecret);
Assert.validURL(frontendAPI, 'frontendAPI');
Assert.validURL(backendAPI, 'backendAPI');

this.ProjectID = projectID;
this.APISecret = apiSecret;
this.Client = DefaultClient;
this.FrontendAPI = DefaultFrontendAPI.replace('[projectID]', projectID);
this.FrontendAPIWithCName = cname ?? this.FrontendAPI;
}

public setFrontendAPI(frontendApi: string): void {
Assert.validURL(frontendApi, 'frontendApi');
this.FrontendAPI = frontendApi;
}

public setBackendAPI(backendAPI: string): void {
Assert.validURL(backendAPI, 'backendAPI');
this.FrontendAPI = frontendAPI;
this.BackendAPI = backendAPI;
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Assert {
);

validate(
parsedUrl.pathname !== '/' && parsedUrl.pathname !== '/v2',
parsedUrl.pathname !== '/',
`${errorName} URL path assertion failed`,
INVALID_URL.code,
'path needs to be empty',
Expand Down
4 changes: 2 additions & 2 deletions src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class SDK {

this.session = new Session(
config.ShortSessionCookieName,
config.FrontendAPIWithCName,
config.FrontendAPI,
`${config.FrontendAPI}/.well-known/jwks`,
config.CacheMaxAge,
config.ProjectID,
Expand All @@ -32,7 +32,7 @@ class SDK {

createClient(config: Config): AxiosInstance {
const instance = axios.create({
baseURL: config.BackendAPI,
baseURL: `${config.BackendAPI}/v2`,
auth: {
username: config.ProjectID,
password: config.APISecret,
Expand Down
40 changes: 26 additions & 14 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,70 @@
import { DefaultBackendAPI, DefaultCacheMaxAge, DefaultShortSessionCookieName } from '../src/config.js';
import { DefaultCacheMaxAge, DefaultShortSessionCookieName } from '../src/config.js';
import { BaseError } from '../src/errors/index.js';
import { Config } from '../src/index.js';

describe('Configuration class', () => {
let projectID: string;
let apiSecret: string;
let frontendAPI: string;
let backendAPI: string;

beforeEach(() => {
projectID = process.env.CORBADO_PROJECT_ID as string; // necessary to mitigate TS error
apiSecret = process.env.CORBADO_API_SECRET as string; // Same here
frontendAPI = process.env.CORBADO_FRONTEND_API as string; // Same here
backendAPI = process.env.CORBADO_BACKEND_API as string; // Same here

if (!projectID || !apiSecret) {
throw new BaseError('Env Error', 5001, 'Both projectID and apiSecret must be defined', true);
}
});

const createAndAssertConfig = (config: Config) => {
expect(config).toBeInstanceOf(Config);
expect(config.ProjectID).toBe(projectID);
expect(config.APISecret).toBe(apiSecret);
expect(config.FrontendAPI).toBe(`https://${projectID}.frontendapi.cloud.corbado.io`);
expect(config.BackendAPI).toBe(DefaultBackendAPI);
expect(config.BackendAPI).toBe(backendAPI);
expect(config.ShortSessionCookieName).toBe(DefaultShortSessionCookieName);
expect(config.CacheMaxAge).toBe(DefaultCacheMaxAge);
};

it('should instantiate Configuration with valid project ID and API secret', () => {
const config = new Config(projectID, apiSecret);
it('should instantiate Configuration with valid project ID and API secret and APIs', () => {
const config = new Config(projectID, apiSecret, frontendAPI, backendAPI);
createAndAssertConfig(config);
});

it('should assign default values to BackendAPI, ShortSessionCookieName, CacheMaxAge, and JWTIssuer', () => {
const config = new Config(projectID, apiSecret);
expect(config.BackendAPI).toBe(DefaultBackendAPI);
const config = new Config(projectID, apiSecret, frontendAPI, backendAPI);
expect(config.BackendAPI).toBe(backendAPI);
expect(config.FrontendAPI).toBe(frontendAPI);
expect(config.ShortSessionCookieName).toBe(DefaultShortSessionCookieName);
expect(config.CacheMaxAge).toBe(DefaultCacheMaxAge);
});

it('should generate DefaultFrontendAPI using process.env.CORBADO_PROJECT_ID and provided project ID', () => {
const config = new Config(projectID, apiSecret);
expect(config.FrontendAPI).toBe(`https://${projectID}.frontendapi.cloud.corbado.io`);
});

it('should throw an error when instantiated with an invalid project ID', () => {
expect(() => new Config('invalid', apiSecret)).toThrow('ProjectID must not be empty and must start with "pro-".');
expect(() => new Config('invalid', apiSecret, frontendAPI, backendAPI)).toThrow(
'ProjectID must not be empty and must start with "pro-".',
);
});

it('should throw an error when instantiated with an invalid API secret', () => {
expect(() => new Config(projectID, 'invalid')).toThrow(
expect(() => new Config(projectID, 'invalid', frontendAPI, backendAPI)).toThrow(
'APISecret must not be empty and must start with "corbado1_".',
);
});

it('should throw an error when project ID is undefined', () => {
expect(() => new Config(undefined as unknown as string, apiSecret)).toThrow(
expect(() => new Config(undefined as unknown as string, apiSecret, frontendAPI, backendAPI)).toThrow(
'ProjectID must not be empty and must start with "pro-".',
);
});

it('should throw an error when frontendAPI is wrong', () => {
expect(() => new Config(projectID, apiSecret, `${frontendAPI}/v2`, backendAPI)).toThrow('path needs to be empty');
});

it('should throw an error when backendAPI is wrong', () => {
expect(() => new Config(projectID, apiSecret, frontendAPI, `${backendAPI}/v2`)).toThrow('path needs to be empty');
});
});
7 changes: 6 additions & 1 deletion tests/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ describe('SDK class', () => {
throw new BaseError('Env Error', 5001, 'Both projectID and apiSecret must be defined', true);
}

config = new Config(projectID, apiSecret);
config = new Config(
projectID,
apiSecret,
`https://${projectID}.frontendapi.cloud.corbado.io`,
`https://backendapi.cloud.corbado.io`,
);
sdk = new SDK(config);
});

Expand Down
7 changes: 6 additions & 1 deletion tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { User } from '../src/generated';

class Utils {
public static SDK(): SDK {
const config = new Config(this.getEnv('CORBADO_PROJECT_ID'), this.getEnv('CORBADO_API_SECRET'));
const config = new Config(
this.getEnv('CORBADO_PROJECT_ID'),
this.getEnv('CORBADO_API_SECRET'),
this.getEnv('CORBADO_FRONTEND_API'),
this.getEnv('CORBADO_BACKEND_API'),
);

return new SDK(config);
}
Expand Down

0 comments on commit 0fbc99d

Please sign in to comment.