Skip to content

Commit a75b97f

Browse files
committed
Added Support for Limit M2M Auth API
1 parent 9d5dcb8 commit a75b97f

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './auth/index.js';
33
export * from './userinfo/index.js';
44
export * from './lib/errors.js';
55
export * from './lib/models.js';
6+
export * from './lib/httpResponseHeadersUtils.js';
67
export * from './deprecations.js';
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
export interface TokenQuotaLimit {
2+
quota: number;
3+
remaining: number;
4+
time: number;
5+
}
6+
7+
export interface TokenQuotaBucket {
8+
perHour?: TokenQuotaLimit;
9+
perDay?: TokenQuotaLimit;
10+
}
11+
12+
export class HttpResponseHeadersUtils {
13+
/**
14+
* Gets the client token quota limits from the provided headers.
15+
*
16+
* @param headers the HTTP response headers.
17+
* @return a TokenQuotaBucket containing client rate limits, or null if not present.
18+
*/
19+
static getClientQuotaLimit(headers: Headers | Record<string, string>): TokenQuotaBucket | null {
20+
const getHeaderValue = (key: string): string | null => {
21+
if (headers instanceof Headers) {
22+
return headers.get(key);
23+
}
24+
return headers[key] || null;
25+
};
26+
27+
const quotaHeader =
28+
getHeaderValue('x-quota-client-limit') || getHeaderValue('auth0-quota-client-limit');
29+
return quotaHeader ? this.parseQuota(quotaHeader) : null;
30+
}
31+
32+
/**
33+
* Gets the organization token quota limits from the provided headers.
34+
*
35+
* @param headers the HTTP response headers.
36+
* @return a TokenQuotaBucket containing organization rate limits, or null if not present.
37+
*/
38+
static getOrganizationQuotaLimit(
39+
headers: Headers | Record<string, string>
40+
): TokenQuotaBucket | null {
41+
const getHeaderValue = (key: string): string | null => {
42+
if (headers instanceof Headers) {
43+
return headers.get(key);
44+
}
45+
return headers[key] || null;
46+
};
47+
48+
const quotaHeader =
49+
getHeaderValue('x-quota-Organization-limit') ||
50+
getHeaderValue('auth0-quota-Organization-limit');
51+
return quotaHeader ? this.parseQuota(quotaHeader) : null;
52+
}
53+
54+
/**
55+
* Parses a token quota string into a TokenQuotaBucket.
56+
*
57+
* @param tokenQuota the token quota string.
58+
* @return a TokenQuotaBucket containing parsed rate limits.
59+
*/
60+
private static parseQuota(tokenQuota: string): TokenQuotaBucket {
61+
let perHour: TokenQuotaLimit | undefined;
62+
let perDay: TokenQuotaLimit | undefined;
63+
64+
const parts = tokenQuota.split(',');
65+
for (const part of parts) {
66+
const attributes = part.split(';');
67+
let quota = 0,
68+
remaining = 0,
69+
time = 0;
70+
71+
for (const attribute of attributes) {
72+
const [key, value] = attribute.split('=').map((s) => s.trim());
73+
if (!key || !value) continue;
74+
75+
switch (key) {
76+
case 'q':
77+
quota = parseInt(value, 10);
78+
break;
79+
case 'r':
80+
remaining = parseInt(value, 10);
81+
break;
82+
case 't':
83+
time = parseInt(value, 10);
84+
break;
85+
}
86+
}
87+
88+
if (attributes[0].includes('per_hour')) {
89+
perHour = { quota, remaining, time };
90+
} else if (attributes[0].includes('per_day')) {
91+
perDay = { quota, remaining, time };
92+
}
93+
}
94+
95+
return { perHour, perDay };
96+
}
97+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { HttpResponseHeadersUtils, TokenQuotaBucket } from '../../src/lib/HttpResponseHeadersUtils';
2+
3+
describe('HttpResponseHeadersUtils', () => {
4+
describe('getClientQuotaLimit', () => {
5+
it('should return a valid TokenQuotaBucket when x-quota-client-limit header is present', () => {
6+
const headers = {
7+
'x-quota-client-limit': 'per_hour;q=100;r=50;t=3600,per_day;q=1000;r=500;t=86400',
8+
};
9+
const result = HttpResponseHeadersUtils.getClientQuotaLimit(headers);
10+
expect(result).toEqual({
11+
perHour: { quota: 100, remaining: 50, time: 3600 },
12+
perDay: { quota: 1000, remaining: 500, time: 86400 },
13+
});
14+
});
15+
16+
it('should return null when no relevant headers are present', () => {
17+
const headers = { 'some-other-header': 'value' };
18+
const result = HttpResponseHeadersUtils.getClientQuotaLimit(headers);
19+
expect(result).toBeNull();
20+
});
21+
});
22+
23+
describe('getOrganizationQuotaLimit', () => {
24+
it('should return a valid TokenQuotaBucket when x-quota-Organization-limit header is present', () => {
25+
const headers = { 'x-quota-Organization-limit': 'per_hour;q=200;r=150;t=3600' };
26+
const result = HttpResponseHeadersUtils.getOrganizationQuotaLimit(headers);
27+
expect(result).toEqual({
28+
perHour: { quota: 200, remaining: 150, time: 3600 },
29+
perDay: undefined,
30+
});
31+
});
32+
33+
it('should return null when no relevant headers are present', () => {
34+
const headers = { 'some-other-header': 'value' };
35+
const result = HttpResponseHeadersUtils.getOrganizationQuotaLimit(headers);
36+
expect(result).toBeNull();
37+
});
38+
});
39+
40+
describe('parseQuota', () => {
41+
it('should correctly parse a token quota string into a TokenQuotaBucket', () => {
42+
const tokenQuota = 'per_hour;q=300;r=250;t=3600,per_day;q=3000;r=2500;t=86400';
43+
const result = HttpResponseHeadersUtils['parseQuota'](tokenQuota);
44+
expect(result).toEqual({
45+
perHour: { quota: 300, remaining: 250, time: 3600 },
46+
perDay: { quota: 3000, remaining: 2500, time: 86400 },
47+
});
48+
});
49+
50+
it('should handle missing attributes gracefully', () => {
51+
const tokenQuota = 'per_hour;q=300;r=250';
52+
const result = HttpResponseHeadersUtils['parseQuota'](tokenQuota);
53+
expect(result).toEqual({
54+
perHour: { quota: 300, remaining: 250, time: 0 },
55+
perDay: undefined,
56+
});
57+
});
58+
59+
it('should return an empty TokenQuotaBucket for invalid input', () => {
60+
const tokenQuota = 'invalid_format';
61+
const result = HttpResponseHeadersUtils['parseQuota'](tokenQuota);
62+
expect(result).toEqual({ perHour: undefined, perDay: undefined });
63+
});
64+
});
65+
});

0 commit comments

Comments
 (0)