Skip to content
This repository has been archived by the owner on Mar 28, 2024. It is now read-only.

Commit

Permalink
feat(nano-server): get user auth
Browse files Browse the repository at this point in the history
  • Loading branch information
njfamirm committed Dec 31, 2023
1 parent f8159ec commit cefda61
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 74 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
],
"cSpell.language": "en,fa,fa-IR",
"cSpell.words": [
"Alwatr"
"alwatr",
"nanoservice"
],
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"git.autoStash": true,
Expand Down
110 changes: 37 additions & 73 deletions packages/nano-server/src/nano-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {createServer} from 'node:http';
import {createLogger, definePackage, type AlwatrLogger} from '@alwatr/logger';
import {isNumber} from '@alwatr/math';

import type {NanoServerConfig, ConnectionConfig} from './type.js';
import type {NanoServerConfig, ConnectionConfig, UserAuth} from './type';
import type {
AlwatrServiceResponse,
AlwatrServiceResponseFailed,
Expand All @@ -16,7 +16,6 @@ import type {
QueryParameters,
Stringifyable,
StringifyableRecord,
UserAuth,
} from '@alwatr/type';
import type {IncomingMessage, ServerResponse} from 'node:http';
import type {Duplex} from 'node:stream';
Expand Down Expand Up @@ -398,19 +397,6 @@ export class AlwatrConnection {
this._logger.logMethodArgs?.('constructor', {method: incomingMessage.method, url: incomingMessage.url});
}

/**
* Get the token placed in the request header.
*/
getAuthBearer(): string | null {
const auth = this.incomingMessage.headers.authorization?.split(' ');

if (auth == null || auth[0].toLowerCase() !== 'bearer') {
return null;
}

return auth[1];
}

/**
* Get request body for POST, PUT and POST methods.
*
Expand Down Expand Up @@ -482,64 +468,57 @@ export class AlwatrConnection {
}

/**
* Parse and validate request token.
* Get the user authentication information from the incoming message headers.
*
* @returns Request token.
* @returns An object containing the user authentication information.
*
* Example:
* @example
* ```ts
* const token = connection.requireToken((token) => token.length > 12);
* if (token == null) return;
* const userAuth = connection.getUserAuth();
* ```
*/
requireToken(validator?: ((token: string) => boolean) | string[] | string): string {
const token = this.getAuthBearer();

if (token == null) {
throw {
ok: false,
statusCode: 401,
errorCode: 'authorization_required',
};
}
else if (validator === undefined) {
return token;
}
else if (typeof validator === 'string') {
if (token === validator) return token;
}
else if (Array.isArray(validator)) {
if (validator.includes(token)) return token;
}
else if (typeof validator === 'function') {
if (validator(token) === true) return token;
}
throw {
ok: false,
statusCode: 403,
errorCode: 'access_denied',
getUserAuth(): Partial<UserAuth> {
const userId = this.incomingMessage.headers['user-id'];
const userToken = this.incomingMessage.headers['user-token'];
const deviceId = this.incomingMessage.headers['device-id'];

return {
userId: userId,

Check failure on line 486 in packages/nano-server/src/nano-server.ts

View workflow job for this annotation

GitHub Actions / Build & Lint Typescript

Type 'string | string[] | undefined' is not assignable to type 'string | undefined'.
userToken: userToken,

Check failure on line 487 in packages/nano-server/src/nano-server.ts

View workflow job for this annotation

GitHub Actions / Build & Lint Typescript

Type 'string | string[] | undefined' is not assignable to type 'string | undefined'.
deviceId: deviceId,

Check failure on line 488 in packages/nano-server/src/nano-server.ts

View workflow job for this annotation

GitHub Actions / Build & Lint Typescript

Type 'string | string[] | undefined' is not assignable to type 'string | undefined'.
};
}

/**
* Parse and get request user auth (include id and token).
* Get and validate the user authentication.
*
* Example:
* @param validator Optional function to validate the user authentication.
* @returns The user authentication information.
* @throws {'access_denied'} If user authentication is missing or validation fails.
*
* @example
* ```ts
* const userAuth = connection.requireUserAuth();
* ```
*/
getUserAuth(): UserAuth | null {
const auth = this.getAuthBearer()
?.split('/')
.filter((item) => item.trim() !== '');

return auth == null || auth.length !== 2
? null
: {
id: auth[0],
token: auth[1],
requireUserAuth(validator?: (userAuth: Partial<UserAuth>) => boolean): UserAuth {
const userAuth = this.getUserAuth();

if (userAuth.userId == null || userAuth.userToken == null || userAuth.deviceId == null) {
throw {
ok: false,
statusCode: 401,
errorCode: 'authorization_required',
};
} else if (typeof validator === 'function' && validator(userAuth) !== true) {
throw {
ok: false,
statusCode: 403,
errorCode: 'access_denied',
};
}

return userAuth as UserAuth;
}

/**
Expand Down Expand Up @@ -622,19 +601,4 @@ export class AlwatrConnection {
'unknown'
);
}

requireClientId(): string {
const clientId = this.incomingMessage.headers['client-id'];

if (!clientId) {
// eslint-disable-next-line no-throw-literal
throw {
ok: false,
statusCode: 401,
errorCode: 'client_denied',
};
}

return clientId;
}
}
6 changes: 6 additions & 0 deletions packages/nano-server/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,9 @@ export interface ConnectionConfig {

export type ParamKeyType = 'string' | 'number' | 'boolean';
export type ParamValueType = string | number | boolean | null;

export interface UserAuth {
userId: string;
userToken: string;
deviceId: string;
}

0 comments on commit cefda61

Please sign in to comment.