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
45 changes: 24 additions & 21 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,30 @@ Features:

### Core Claims

| Claim | Validate |
| --------------------------------------------------------- | -------- |
| Issuer (`iss`) | Yes |
| Audience (`aud`) | Yes |
| Expiration (`exp`) | Yes |
| Not Before (`nbf`) | Yes |
| CWT ID (`cti`) | Yes |
| Common Access Token Replay (`catreplay`) | Yes |
| Common Access Token Probability of Rejection (`catpor`) | No |
| Common Access Token Version (`catv`) | Yes |
| Common Access Token Network IP (`catnip`) | No |
| Common Access Token URI (`catu`) | Yes |
| Common Access Token Methods (`catm`) | Yes |
| Common Access Token ALPN (`catalpn`) | No |
| Common Access Token Header (`cath`) | No |
| Common Access Token Geographic ISO3166 (`catgeoiso3166`) | No |
| Common Access Token Geographic Coordinate (`catgeocoord`) | No |
| Geohash (`geohash`) | No |
| Common Access Token Altitude (`catgeoalt`) | No |
| Common Access Token TLS Public Key (`cattpk`) | No |
| Common Access Token Renewal (`catr`) claim | Yes |
| Claim | Pass-through | Validate |
| --------------------------------------------------------- | ------------ | -------- |
| Issuer (`iss`) | Yes | Yes |
| Audience (`aud`) | Yes | Yes |
| Expiration (`exp`) | Yes | Yes |
| Not Before (`nbf`) | Yes | Yes |
| CWT ID (`cti`) | Yes | Yes |
| Subject (`sub`) | Yes | n/a |
| Issued at (`iat`) | Yes | n/a |
| Common Access Token Replay (`catreplay`) | Yes | Yes |
| Common Access Token Probability of Rejection (`catpor`) | Yes | No |
| Common Access Token Version (`catv`) | Yes | Yes |
| Common Access Token Network IP (`catnip`) | Yes | No |
| Common Access Token URI (`catu`) | Yes | Yes |
| Common Access Token Methods (`catm`) | Yes | Yes |
| Common Access Token ALPN (`catalpn`) | No | No |
| Common Access Token Header (`cath`) | Yes | No |
| Common Access Token Geographic ISO3166 (`catgeoiso3166`) | Yes | No |
| Common Access Token Geographic Coordinate (`catgeocoord`) | Yes | No |
| Geohash (`geohash`) | Yes | No |
| Common Access Token Altitude (`catgeoalt`) | Yes | No |
| Common Access Token TLS Public Key (`cattpk`) | Yes | No |
| Common Access Token If (`catif`) claim | Yes | No |
| Common Access Token Renewal (`catr`) claim | Yes | No |

## Requirements

Expand Down
97 changes: 89 additions & 8 deletions src/cat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
InvalidAudienceError,
InvalidClaimTypeError,
InvalidIssuerError,
InvalidJsonError,
RenewalClaimError,
TokenExpiredError,
TokenNotActiveError,
Expand All @@ -12,8 +13,10 @@
import { CatValidationOptions } from '.';
import { CommonAccessTokenUri } from './catu';
import { CommonAccessTokenRenewal } from './catr';
import { CommonAccessTokenHeader } from './cath';
import { CommonAccessTokenIf } from './catif';

const claimsToLabels: { [key: string]: number } = {
export const claimsToLabels: { [key: string]: number } = {
iss: 1, // 3
sub: 2, // 3
aud: 3, // 3
Expand All @@ -22,6 +25,7 @@
iat: 6, // 6 tag value 1
cti: 7, // 2,
cnf: 8,
geohash: 282,
catreplay: 308,
catpor: 309,
catv: 310,
Expand All @@ -39,7 +43,7 @@
catr: 323
};

const labelsToClaim: { [key: number]: string } = {
export const labelsToClaim: { [key: number]: string } = {
1: 'iss',
2: 'sub',
3: 'aud',
Expand All @@ -48,6 +52,7 @@
6: 'iat',
7: 'cti',
8: 'cnf',
282: 'geohash',
308: 'catreplay',
309: 'catpor',
310: 'catv',
Expand Down Expand Up @@ -75,11 +80,64 @@
cattpk: (value: Buffer) => value.toString('hex')
};

const claimTypeValidators: { [key: string]: (value: string) => boolean } = {
const claimTypeValidators: {
[key: string]: (value: CommonAccessTokenValue) => boolean;
} = {
iss: (value) => typeof value === 'string',
exp: (value) => typeof value === 'number',
aud: (value) => typeof value === 'string' || Array.isArray(value),
nbf: (value) => typeof value === 'number'
nbf: (value) => typeof value === 'number',
cti: (value) => Buffer.isBuffer(value),
catreplay: (value) => typeof value === 'number',
catpor: (value) => Array.isArray(value),
catv: (value) => typeof value === 'number' && value >= 1,
catnip: (value) => typeof value === 'number' || typeof value === 'string',
catu: (value) => value instanceof Map,
catm: (value) => Array.isArray(value),
cath: (value) => value instanceof Map,
catgeoiso3166: (value) => Array.isArray(value),
catgeocoord: (value) => Array.isArray(value),
cattpk: (value) => Buffer.isBuffer(value),
sub: (value) => typeof value === 'string',
iat: (value) => typeof value === 'number',
catifdata: (value) => typeof value === 'string' || Array.isArray(value),
cnf: (value) => value instanceof Map,
catdpop: (value) => value instanceof Map,
catif: (value) => value instanceof Map,
catr: (value) => value instanceof Map
};

const isHex = (value: string) => /^[0-9a-fA-F]+$/.test(value);
const isNetworkIp = (value: string) => /^[0-9a-fA-F:.]+$/.test(value);

const claimTypeDictValidators: {
[key: string]: (value: unknown) => boolean;
} = {
iss: (value) => typeof value === 'string',
exp: (value) => typeof value === 'number',
aud: (value) => typeof value === 'string' || Array.isArray(value),
nbf: (value) => typeof value === 'number',
cti: (value) => typeof value === 'string' && isHex(value),
catreplay: (value) => typeof value === 'number' && value >= 0,
catpor: (value) => Array.isArray(value),
catv: (value) => typeof value === 'number' && value >= 1,
catnip: (value) =>
typeof value === 'number' ||
(typeof value === 'string' && isNetworkIp(value)),
catu: (value) => typeof value === 'object',
catm: (value) => Array.isArray(value),
cath: (value) => typeof value === 'object',
catgeoiso3166: (value) => Array.isArray(value),
catgeocoord: (value) => Array.isArray(value),
catgeoalt: (value) => Array.isArray(value),
cattpk: (value) => typeof value === 'string' && isHex(value),
sub: (value) => typeof value === 'string',
iat: (value) => typeof value === 'number',
catifdata: (value) => typeof value === 'string' || Array.isArray(value),
cnf: (value) => typeof value === 'object',
catdpop: (value) => typeof value === 'object',
catif: (value) => typeof value === 'object',
catr: (value) => typeof value === 'object'
};

const CWT_TAG = 61;
Expand All @@ -88,16 +146,16 @@
* Common Access Token Claims
*/
export type CommonAccessTokenClaims = {
[key: string]: string | number | Map<number, any>;
[key: string]: string | number | Map<number | string, any>;

Check warning on line 149 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
};
export type CommonAccessTokenDict = {
[key: string]: string | number | { [key: string]: any };

Check warning on line 152 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
};
export type CommonAccessTokenValue =
| string
| number
| Buffer
| Map<number, any>;
| Map<number | string, any>;

Check warning on line 158 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

/**
* CWT Encryption Key
Expand Down Expand Up @@ -161,15 +219,29 @@
): CommonAccessTokenClaims {
const claims: CommonAccessTokenClaims = {};
for (const param in dict) {
if (
claimTypeDictValidators[param] &&
!claimTypeDictValidators[param](dict[param])
) {
throw new InvalidJsonError(param);
}
const key = claimsToLabels[param];
if (param === 'catu') {
claims[key] = CommonAccessTokenUri.fromDict(
dict[param] as { [key: string]: any }

Check warning on line 231 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
).payload;
} else if (param === 'catr') {
claims[key] = CommonAccessTokenRenewal.fromDict(
dict[param] as { [key: string]: any }

Check warning on line 235 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
).payload;
} else if (param == 'cath') {
claims[key] = CommonAccessTokenHeader.fromDict(
dict[param] as { [key: string]: any }

Check warning on line 239 in src/cat.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
).payload;
} else if (param == 'catif') {
claims[key] = CommonAccessTokenIf.fromDict(
dict[param] as { [key: string]: any }
).payload;
} else {
const value = claimTransform[param]
? claimTransform[param](dict[param] as string)
Expand All @@ -193,6 +265,7 @@
claims['catv'] = 1;
}
this.payload = updateMapFromClaims(claims);
this.validateTypes();
}

/**
Expand Down Expand Up @@ -290,8 +363,8 @@
for (const [key, value] of this.payload) {
const claim = labelsToClaim[key];
if (value && claimTypeValidators[claim]) {
if (!claimTypeValidators[claim](value as string)) {
throw new InvalidClaimTypeError(claim, value as string);
if (!claimTypeValidators[claim](value)) {
throw new InvalidClaimTypeError(claim, value);
}
}
}
Expand Down Expand Up @@ -396,6 +469,14 @@
result[key] = CommonAccessTokenRenewal.fromMap(
value as Map<number, any>
).toDict();
} else if (key === 'cath') {
result[key] = CommonAccessTokenHeader.fromMap(
value as Map<string, any>
).toDict();
} else if (key === 'catif') {
result[key] = CommonAccessTokenIf.fromMap(
value as Map<number, any>
).toDict();
} else {
const theValue = claimTransformReverse[key]
? claimTransformReverse[key](value as Buffer)
Expand Down
46 changes: 46 additions & 0 deletions src/cath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
labelsToMatch,
MatchMap,
matchToLabels,
MatchValue
} from './cattypes/match';

export type CommonAccessTokenHeaderMap = Map<string, MatchMap>;

export class CommonAccessTokenHeader {
private cathMap: CommonAccessTokenHeaderMap = new Map();

public static fromDict(dict: { [key: string]: any }) {
const cath = new CommonAccessTokenHeader();
for (const header in dict) {
const matchMap = new Map<number, MatchValue>();
for (const match in dict[header]) {
matchMap.set(matchToLabels[match], dict[header][match]);
}
cath.cathMap.set(header, matchMap);
}
return cath;
}

public static fromMap(map: CommonAccessTokenHeaderMap) {
const cath = new CommonAccessTokenHeader();
cath.cathMap = map;
return cath;
}

toDict() {
const result: { [key: string]: any } = {};
this.cathMap.forEach((matchMap, header) => {
const match: { [key: string]: any } = {};
matchMap.forEach((value, matchType) => {
match[labelsToMatch[matchType]] = value;
});
result[header] = match;
});
return result;
}

get payload() {
return this.cathMap;
}
}
34 changes: 34 additions & 0 deletions src/catif.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { claimsToLabels, labelsToClaim } from './cat';

type CatIfValue = Map<number, [number, { [key: string]: string }]>;
export type CommonAccessTokenIfMap = Map<number, CatIfValue>;

export class CommonAccessTokenIf {
private catIfMap: CommonAccessTokenIfMap = new Map();

public static fromDict(dict: { [key: string]: any }) {
const catif = new CommonAccessTokenIf();
for (const catIfClaim in dict) {
catif.catIfMap.set(claimsToLabels[catIfClaim], dict[catIfClaim]);
}
return catif;
}

public static fromMap(map: CommonAccessTokenIfMap) {
const catif = new CommonAccessTokenIf();
catif.catIfMap = map;
return catif;
}

toDict() {
const result: { [key: string]: any } = {};
this.catIfMap.forEach((catIfValue, claim) => {
result[labelsToClaim[claim]] = catIfValue;
});
return result;
}

get payload() {
return this.catIfMap;
}
}
Loading
Loading