Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Config changes #32

Merged
merged 5 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
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_BACKEND_API=https://[project-id].frontendapi.cloud.corbado.io
Dopeamin marked this conversation as resolved.
Show resolved Hide resolved
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 APIs should be domain names and the pathname should be empty. If these conditions are not met, an error is thrown.
Dopeamin marked this conversation as resolved.
Show resolved Hide resolved

## 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: 6 additions & 16 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface ConfigInterface {
}

export const DefaultClient = axios.create();
export const DefaultBackendAPI = 'https://backendapi.cloud.corbado.io/v2';
export const DefaultBackendAPI = 'https://backendapi.cloud.corbado.io';
export const DefaultFrontendAPI = 'https://[projectID].frontendapi.cloud.corbado.io';
Dopeamin marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -22,9 +22,7 @@ class Config implements ConfigInterface {

APISecret: string;

FrontendAPI: string;

FrontendAPIWithCName: string;
FrontendAPI: string = DefaultFrontendAPI;

BackendAPI: string = DefaultBackendAPI;

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

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
36 changes: 24 additions & 12 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ 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);
Expand All @@ -23,36 +29,42 @@ describe('Configuration class', () => {
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
Loading