Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 11 additions & 5 deletions examples/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import http from 'node:http';

import { HttpValidator, RedisCTIStore } from '../src';
import {
ConsoleLogger,
HttpValidator,
MemoryCTIStore,
RedisCTIStore
} from '../src';

const store = process.env.REDIS_URL
? new RedisCTIStore(new URL(process.env.REDIS_URL))
: new MemoryCTIStore();
const httpValidator = new HttpValidator({
keys: [
{
Expand All @@ -13,14 +21,12 @@ const httpValidator = new HttpValidator({
}
],
issuer: 'eyevinn',
store: new RedisCTIStore(
new URL(process.env.REDIS_URL || 'redis://localhost:6379')
)
store,
logger: new ConsoleLogger()
});

const server = http.createServer(async (req, res) => {
const result = await httpValidator.validateHttpRequest(req, res);
console.log(result);
res.writeHead(result.status, { 'Content-Type': 'text/plain' });
res.end(result.message || 'ok');
});
Expand Down
37 changes: 36 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Features:
- Validation and parsing of tokens
- Handle automatic renewal of tokens
- Token usage count using store plugins (see further down for available plugins)
- Log how tokens are being used with logging plugins (see further down for available plugins)

## Claims Validation Support

Expand Down Expand Up @@ -85,7 +86,8 @@ const httpValidator = new HttpValidator({
tokenMandatory: true // Optional (default: true)
issuer: 'eyevinn',
audience: ['one', 'two'], // Optional
store: new RedisCTIStore(new URL(process.env.REDIS_URL || 'redis://localhost:6379')) // Where to store token usage count. Optional (default: none)
store: new RedisCTIStore(new URL(process.env.REDIS_URL || 'redis://localhost:6379')), // Where to store token usage count. Optional (default: none)
logger: new ConsoleLogger() // Adapter to store all tokens that been successfully validated. Optional (default: none)
});

const server = http.createServer((req, res) => {
Expand Down Expand Up @@ -268,6 +270,39 @@ class MyStore implements ICTIStore {
}
```

## Token logging plugin

Log how tokens are being used to be able to analyze detect usage anamolies with a token logging plugin. The following logging plugins are provided today.

### Console Logger

This just logs the token to stdout in a JSON format.

```javascript
const logging = new ConsoleLogging();
// Will output for example
// {"cti":"b43c9cef64ca7dc83af8a33f39fc7168","timestamp":1742386792234,"iat":1742386782,"exp":1742386902,"sub":"jonas"}
// {"cti":"b43c9cef64ca7dc83af8a33f39fc7168","timestamp":1742386799501,"iat":1742386782,"exp":1742386902,"sub":"jonas"}
```

### Custom Logger

To implement your own logger you implement the interface `ITokenLogger`, for example:

```javascript
import { ITokenLogger, CommonAccessToken } from '@eyvinn/cat';

// Pseudo code below
class MyLogger implements ITokenLogger {
async logToken(token: CommonAccessToken): Promise<void> {
if (!this.db.connected) {
await this.db.connect();
}
this.db.insert(token);
}
}
```

## Development

<!--Add clear instructions on how to start development of the project here -->
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export { ICTIStore } from './stores/interface';
export { MemoryCTIStore } from './stores/memory';
export { RedisCTIStore } from './stores/redis';

export { ITokenLogger } from './loggers/interface';
export { ConsoleLogger } from './loggers/console';

export type CatValidationTypes = 'mac' | 'sign' | 'none';

export interface CatValidationOptions {
Expand Down
15 changes: 15 additions & 0 deletions src/loggers/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CommonAccessToken } from '..';
import { ITokenLogger } from './interface';

export class ConsoleLogger implements ITokenLogger {
async logToken(token: CommonAccessToken): Promise<void> {
const json = {
cti: token.cti,
timestamp: Date.now(),
iat: token.claims.iat,
exp: token.claims.exp,
sub: token.claims.sub
};
console.log(JSON.stringify(json));
}
}
5 changes: 5 additions & 0 deletions src/loggers/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CommonAccessToken } from '..';

export interface ITokenLogger {
logToken(token: CommonAccessToken): Promise<void>;
}
7 changes: 7 additions & 0 deletions src/validators/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from 'aws-lambda';
import { CommonAccessTokenDict } from '../cat';
import { ICTIStore } from '../stores/interface';
import { ITokenLogger } from '../loggers/interface';

interface HttpValidatorKey {
kid: string;
Expand All @@ -29,6 +30,7 @@ export interface HttpValidatorOptions {
issuer: string;
audience?: string[];
store?: ICTIStore;
logger?: ITokenLogger;
}

export interface HttpResponse {
Expand Down Expand Up @@ -73,6 +75,7 @@ export class HttpValidator {
private opts: HttpValidatorOptions;
private tokenUriParam: string;
private store?: ICTIStore;
private logger?: ITokenLogger;

constructor(opts: HttpValidatorOptions) {
opts.keys.forEach((k: HttpValidatorKey) => {
Expand All @@ -83,6 +86,7 @@ export class HttpValidator {
this.opts.autoRenewEnabled = opts.autoRenewEnabled ?? true;
this.tokenUriParam = opts.tokenUriParam ?? 'cat';
this.store = opts.store;
this.logger = opts.logger;
}

public async validateCloudFrontRequest(
Expand Down Expand Up @@ -165,6 +169,9 @@ export class HttpValidator {
if (cat && this.store) {
count = await this.store.storeToken(cat);
}
if (cat && this.logger) {
await this.logger.logToken(cat);
}
if (
cat &&
cat?.shouldRenew &&
Expand Down
Loading