@picahq/authkit-token 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +28 -0
- package/LICENSE +674 -0
- package/README.MD +77 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/src/apis/index.d.ts +2 -0
- package/dist/src/apis/index.d.ts.map +1 -0
- package/dist/src/apis/index.js +18 -0
- package/dist/src/apis/index.js.map +1 -0
- package/dist/src/apis/link.api.d.ts +3 -0
- package/dist/src/apis/link.api.d.ts.map +1 -0
- package/dist/src/apis/link.api.js +71 -0
- package/dist/src/apis/link.api.js.map +1 -0
- package/dist/src/client/index.d.ts +21 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +32 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/logic/getHeaders.d.ts +5 -0
- package/dist/src/logic/getHeaders.d.ts.map +1 -0
- package/dist/src/logic/getHeaders.js +11 -0
- package/dist/src/logic/getHeaders.js.map +1 -0
- package/index.ts +1 -0
- package/package.json +51 -0
- package/src/apis/index.ts +1 -0
- package/src/apis/link.api.ts +123 -0
- package/src/client/index.ts +54 -0
- package/src/logic/getHeaders.ts +6 -0
- package/src/types/index.d.ts +221 -0
- package/tsconfig.json +29 -0
package/README.MD
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# authkit-token
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/@picahq/authkit-token)
|
|
4
|
+
|
|
5
|
+
Secure token generation for [Pica's AuthKit](https://docs.picaos.com/authkit) using Node.js.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
With npm:
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
npm i @picahq/authkit-token
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
With yarn:
|
|
16
|
+
|
|
17
|
+
```jsx
|
|
18
|
+
yarn add @picahq/authkit-token
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Creating a token endpoint
|
|
22
|
+
|
|
23
|
+
You'll want to create an internal endpoint that's used to generate secure tokens for your frontend. You can do so by adding code that looks like the below snippet.
|
|
24
|
+
|
|
25
|
+
```jsx
|
|
26
|
+
import { AuthKitToken } from "@picahq/authkit-token";
|
|
27
|
+
|
|
28
|
+
app.post("/authkit-token", async (request, response) => {
|
|
29
|
+
const authKitToken = new AuthKitToken("sk_live_1234");
|
|
30
|
+
const token = await authKitToken.create();
|
|
31
|
+
|
|
32
|
+
response.send(token);
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You can get your API key from the [Pica dashboard](https://dashboard.picaos.com/settings/api-keys).
|
|
37
|
+
|
|
38
|
+
If you pass an `identity` or `identityType` (`user`, `team`, `organization`, or `project`), you'll be able to query for all connections scoped to that identity. The identity is used to generate the unique Connection Key for the user once they successfully connect an account.
|
|
39
|
+
|
|
40
|
+
## Frontend Implementation
|
|
41
|
+
|
|
42
|
+
To implement the AuthKit component in your frontend, you'll need to use the `@picahq/authkit` package. It's fully compatible with popular frameworks such as React, Next.js, Vue, Svelte, and more.
|
|
43
|
+
|
|
44
|
+
- **NPM package**: https://www.npmjs.com/package/@picahq/authkit
|
|
45
|
+
- **Documentation**: https://docs.picaos.com/authkit
|
|
46
|
+
- **Setup guide**: https://docs.picaos.com/authkit/setup
|
|
47
|
+
|
|
48
|
+
## Diagram
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
sequenceDiagram
|
|
52
|
+
participant User
|
|
53
|
+
participant YourApp as Your Application
|
|
54
|
+
participant YourBackend as Your Backend
|
|
55
|
+
participant Pica as Pica AuthKit
|
|
56
|
+
participant Integration as Third-party Integration
|
|
57
|
+
|
|
58
|
+
User->>YourApp: Clicks "Connect Integration"
|
|
59
|
+
YourApp->>Pica: Open AuthKit modal
|
|
60
|
+
Pica->>YourBackend: Request AuthKit token
|
|
61
|
+
YourBackend->>Pica: Generate token with user identity
|
|
62
|
+
Pica->>Pica: Display integrations list
|
|
63
|
+
User->>Pica: Select integration & authenticate
|
|
64
|
+
Pica->>Integration: OAuth handshake
|
|
65
|
+
Integration->>Pica: Access token
|
|
66
|
+
Pica->>Pica: Store encrypted credentials
|
|
67
|
+
Pica->>YourApp: Return connection details
|
|
68
|
+
YourApp->>User: Connection successful!
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Full Documentation
|
|
72
|
+
|
|
73
|
+
Please refer to the official [Pica AuthKit](https://docs.picaos.com/authkit) docs for a more holistic understanding of AuthKit.
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
GPL-3.0
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./src/client"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/apis/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./link.api"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/apis/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.api.d.ts","sourceRoot":"","sources":["../../../src/apis/link.api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,EAGvB,MAAM,UAAU,CAAC;AAgGlB,eAAO,MAAM,uBAAuB,YACzB,OAAO,MAAM,EAAE,MAAM,CAAC,OAC1B,MAAM,YACD,sBAAsB,iBAkBjC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createEventLinkTokenApi = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
async function paginateAuthkitConnections(url, headers, payload, options = {}) {
|
|
9
|
+
const { limit = 100, maxConcurrentRequests = 3, maxRetries = 3 } = options;
|
|
10
|
+
const fetchAuthkitPage = async (page, pageLimit) => {
|
|
11
|
+
const response = await axios_1.default.post(`${url}/v1/authkit?limit=${pageLimit}&page=${page}`, payload || {}, { headers });
|
|
12
|
+
return response.data;
|
|
13
|
+
};
|
|
14
|
+
const firstResponse = await fetchAuthkitPage(1, limit);
|
|
15
|
+
const { pages, total } = firstResponse;
|
|
16
|
+
if (pages <= 1) {
|
|
17
|
+
return firstResponse;
|
|
18
|
+
}
|
|
19
|
+
const remainingPages = Array.from({ length: pages - 1 }, (_, i) => i + 2);
|
|
20
|
+
const fetchPageWithRetry = async (page) => {
|
|
21
|
+
let lastError;
|
|
22
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetchAuthkitPage(page, limit);
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
lastError = error;
|
|
29
|
+
if (attempt < maxRetries) {
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw lastError;
|
|
35
|
+
};
|
|
36
|
+
const responses = [firstResponse];
|
|
37
|
+
for (let i = 0; i < remainingPages.length; i += maxConcurrentRequests) {
|
|
38
|
+
const batch = remainingPages.slice(i, i + maxConcurrentRequests);
|
|
39
|
+
const batchPromises = batch.map((page) => fetchPageWithRetry(page));
|
|
40
|
+
try {
|
|
41
|
+
const batchResults = await Promise.all(batchPromises);
|
|
42
|
+
responses.push(...batchResults);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`Failed to fetch authkit batch starting at page ${batch[0]}:`, error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const allRows = responses.flatMap((response) => response.rows);
|
|
50
|
+
const latestResponse = responses[responses.length - 1];
|
|
51
|
+
return {
|
|
52
|
+
rows: allRows,
|
|
53
|
+
page: 1,
|
|
54
|
+
pages: 1,
|
|
55
|
+
total,
|
|
56
|
+
requestId: latestResponse.requestId,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const createEventLinkTokenApi = async (headers, url, payload) => {
|
|
60
|
+
try {
|
|
61
|
+
const authkitResponse = await paginateAuthkitConnections(url, headers, payload, { limit: 100, maxConcurrentRequests: 3, maxRetries: 3 });
|
|
62
|
+
return authkitResponse;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
66
|
+
return error.response?.data;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
exports.createEventLinkTokenApi = createEventLinkTokenApi;
|
|
71
|
+
//# sourceMappingURL=link.api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.api.js","sourceRoot":"","sources":["../../../src/apis/link.api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface ClientConfig {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
}
|
|
4
|
+
export declare class AuthKitToken {
|
|
5
|
+
private secret;
|
|
6
|
+
private configs;
|
|
7
|
+
constructor(secret: string, configs?: ClientConfig);
|
|
8
|
+
get _clientInfo(): {
|
|
9
|
+
secret: string;
|
|
10
|
+
configs: ClientConfig;
|
|
11
|
+
};
|
|
12
|
+
get _url(): string;
|
|
13
|
+
create(payload?: {
|
|
14
|
+
identity?: string;
|
|
15
|
+
identityType?: "user" | "team" | "organization" | "project";
|
|
16
|
+
group?: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
}): Promise<any>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAGA,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAe;gBAElB,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAQtD,IAAI,WAAW;;;MAKd;IAED,IAAI,IAAI,WAMP;IAEK,MAAM,CAAC,OAAO,CAAC,EAAE;QAKrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;QAE5D,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB;CAQF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthKitToken = void 0;
|
|
4
|
+
const getHeaders_1 = require("../logic/getHeaders");
|
|
5
|
+
const apis_1 = require("../apis");
|
|
6
|
+
class AuthKitToken {
|
|
7
|
+
constructor(secret, configs = {}) {
|
|
8
|
+
this.secret = secret;
|
|
9
|
+
this.configs = configs;
|
|
10
|
+
}
|
|
11
|
+
get _clientInfo() {
|
|
12
|
+
return {
|
|
13
|
+
secret: this.secret,
|
|
14
|
+
configs: this.configs,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
get _url() {
|
|
18
|
+
if (this.configs.baseUrl) {
|
|
19
|
+
return this.configs.baseUrl;
|
|
20
|
+
}
|
|
21
|
+
return 'https://api.picaos.com';
|
|
22
|
+
}
|
|
23
|
+
async create(payload) {
|
|
24
|
+
const secret = this._clientInfo.secret;
|
|
25
|
+
const url = this._url;
|
|
26
|
+
const headers = (0, getHeaders_1.getHeaders)(secret);
|
|
27
|
+
const result = await (0, apis_1.createEventLinkTokenApi)(headers, url, payload);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.AuthKitToken = AuthKitToken;
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":";;;AAAA,oDAAiD;AACjD,kCAAkD;AAMlD,MAAa,YAAY;IAIvB,YAAY,MAAc,EAAE,UAAwB,EAAE;QACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAKD,IAAI,WAAW;QACb,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI;QACN,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YACxB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;SAC7B;QAED,OAAO,wBAAwB,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAWZ;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,MAAM,OAAO,GAAG,IAAA,uBAAU,EAAC,MAAM,CAAC,CAAC;QAEnC,MAAM,MAAM,GAAG,MAAM,IAAA,8BAAuB,EAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA9CD,oCA8CC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getHeaders.d.ts","sourceRoot":"","sources":["../../../src/logic/getHeaders.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,WAAY,MAAM;;;CAKxC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHeaders = void 0;
|
|
4
|
+
const getHeaders = (secret) => {
|
|
5
|
+
return {
|
|
6
|
+
'X-Pica-Secret': secret,
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
exports.getHeaders = getHeaders;
|
|
11
|
+
//# sourceMappingURL=getHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getHeaders.js","sourceRoot":"","sources":["../../../src/logic/getHeaders.ts"],"names":[],"mappings":";;;AAAO,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE;IAC3C,OAAO;QACL,eAAe,EAAE,MAAM;QACvB,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC,CAAC;AALW,QAAA,UAAU,cAKrB"}
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/client';
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@picahq/authkit-token",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Secure token generation for Pica AuthKit using Node.js",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc --build tsconfig.json",
|
|
8
|
+
"test": "jest --coverage"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pica",
|
|
15
|
+
"integrations",
|
|
16
|
+
"authkit"
|
|
17
|
+
],
|
|
18
|
+
"author": "@picahq",
|
|
19
|
+
"license": "GPL-3.0",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@types/ramda": "^0.28.22",
|
|
22
|
+
"axios": "^1.6.7",
|
|
23
|
+
"jsonwebtoken": "^9.0.1",
|
|
24
|
+
"jwt-decode": "^3.1.2",
|
|
25
|
+
"lodash.ismatch": "^4.4.0",
|
|
26
|
+
"lodash.memoize": "^4.1.2",
|
|
27
|
+
"ramda": "^0.28.0",
|
|
28
|
+
"tslib": "^2.4.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@babel/core": "^7.17.9",
|
|
32
|
+
"@babel/preset-env": "^7.16.11",
|
|
33
|
+
"@babel/preset-typescript": "^7.16.7",
|
|
34
|
+
"@types/jsonwebtoken": "^9.0.2",
|
|
35
|
+
"@types/lodash.ismatch": "^4.4.7",
|
|
36
|
+
"@types/lodash.memoize": "^4.1.7",
|
|
37
|
+
"@types/uuid": "^8.3.4",
|
|
38
|
+
"babel-jest": "^27.5.1",
|
|
39
|
+
"jest": "^27.5.1",
|
|
40
|
+
"jsonwebtoken": "^9.0.1",
|
|
41
|
+
"ts-jest": "^27.1.4",
|
|
42
|
+
"typescript": "^4.6.4",
|
|
43
|
+
"uuid": "^9.0.0"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "35ff51f0afc618c5413ef9bab778f4dcf582b8ab",
|
|
49
|
+
"homepage": "https://picaos.com",
|
|
50
|
+
"documentation": "https://docs.picaos.com/authkit"
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './link.api';
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
CreateEventLinkPayload,
|
|
4
|
+
AuthkitResponse,
|
|
5
|
+
ConnectorPaginationOptions,
|
|
6
|
+
} from '../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pagination helper function for authkit API that fetches all pages
|
|
10
|
+
* @param url - Base URL for the API
|
|
11
|
+
* @param headers - Request headers
|
|
12
|
+
* @param payload - Request payload
|
|
13
|
+
* @param options - Pagination options
|
|
14
|
+
* @returns Promise with all combined results
|
|
15
|
+
*/
|
|
16
|
+
async function paginateAuthkitConnections(
|
|
17
|
+
url: string,
|
|
18
|
+
headers: Record<string, string>,
|
|
19
|
+
payload?: CreateEventLinkPayload,
|
|
20
|
+
options: ConnectorPaginationOptions = {},
|
|
21
|
+
): Promise<AuthkitResponse> {
|
|
22
|
+
const { limit = 100, maxConcurrentRequests = 3, maxRetries = 3 } = options;
|
|
23
|
+
|
|
24
|
+
// Function to fetch a specific page
|
|
25
|
+
const fetchAuthkitPage = async (page: number, pageLimit: number): Promise<AuthkitResponse> => {
|
|
26
|
+
const response = await axios.post<AuthkitResponse>(
|
|
27
|
+
`${url}/v1/authkit?limit=${pageLimit}&page=${page}`,
|
|
28
|
+
payload || {},
|
|
29
|
+
{ headers }
|
|
30
|
+
);
|
|
31
|
+
return response.data;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// First request to get total pages count
|
|
35
|
+
const firstResponse = await fetchAuthkitPage(1, limit);
|
|
36
|
+
const { pages, total } = firstResponse;
|
|
37
|
+
|
|
38
|
+
// If we got all data in first request, return it
|
|
39
|
+
if (pages <= 1) {
|
|
40
|
+
return firstResponse;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create array of remaining page numbers to fetch
|
|
44
|
+
const remainingPages = Array.from({ length: pages - 1 }, (_, i) => i + 2);
|
|
45
|
+
|
|
46
|
+
// Function to fetch a page with retry logic
|
|
47
|
+
const fetchPageWithRetry = async (page: number): Promise<AuthkitResponse> => {
|
|
48
|
+
let lastError: any;
|
|
49
|
+
|
|
50
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
51
|
+
try {
|
|
52
|
+
const response = await fetchAuthkitPage(page, limit);
|
|
53
|
+
return response;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
lastError = error;
|
|
56
|
+
if (attempt < maxRetries) {
|
|
57
|
+
// Exponential backoff: wait 1s, 2s, 4s...
|
|
58
|
+
await new Promise((resolve) =>
|
|
59
|
+
setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw lastError;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Execute requests in batches to avoid overwhelming the API
|
|
69
|
+
const responses: AuthkitResponse[] = [firstResponse];
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < remainingPages.length; i += maxConcurrentRequests) {
|
|
72
|
+
const batch = remainingPages.slice(i, i + maxConcurrentRequests);
|
|
73
|
+
const batchPromises = batch.map((page) => fetchPageWithRetry(page));
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const batchResults = await Promise.all(batchPromises);
|
|
77
|
+
responses.push(...batchResults);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(
|
|
80
|
+
`Failed to fetch authkit batch starting at page ${batch[0]}:`,
|
|
81
|
+
error,
|
|
82
|
+
);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Combine all results
|
|
88
|
+
const allRows = responses.flatMap((response) => response.rows);
|
|
89
|
+
|
|
90
|
+
// Get the latest requestId from the most recent response
|
|
91
|
+
const latestResponse = responses[responses.length - 1];
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
rows: allRows,
|
|
95
|
+
page: 1, // Since we're returning all data, we're effectively on "page 1"
|
|
96
|
+
pages: 1,
|
|
97
|
+
total,
|
|
98
|
+
requestId: latestResponse.requestId, // Use the requestId from the latest response
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const createEventLinkTokenApi = async (
|
|
103
|
+
headers: Record<string, string>,
|
|
104
|
+
url: string,
|
|
105
|
+
payload?: CreateEventLinkPayload,
|
|
106
|
+
) => {
|
|
107
|
+
try {
|
|
108
|
+
|
|
109
|
+
// Fetch all authkit connections with pagination support
|
|
110
|
+
const authkitResponse = await paginateAuthkitConnections(
|
|
111
|
+
url,
|
|
112
|
+
headers,
|
|
113
|
+
payload,
|
|
114
|
+
{ limit: 100, maxConcurrentRequests: 3, maxRetries: 3 }
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return authkitResponse;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (axios.isAxiosError(error)) {
|
|
120
|
+
return error.response?.data;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getHeaders } from '../logic/getHeaders';
|
|
2
|
+
import { createEventLinkTokenApi } from '../apis';
|
|
3
|
+
|
|
4
|
+
interface ClientConfig {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class AuthKitToken {
|
|
9
|
+
private secret: string;
|
|
10
|
+
private configs: ClientConfig;
|
|
11
|
+
|
|
12
|
+
constructor(secret: string, configs: ClientConfig = {}) {
|
|
13
|
+
this.secret = secret;
|
|
14
|
+
this.configs = configs;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Not for use outside the SDK lib
|
|
19
|
+
*/
|
|
20
|
+
get _clientInfo() {
|
|
21
|
+
return {
|
|
22
|
+
secret: this.secret,
|
|
23
|
+
configs: this.configs,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get _url() {
|
|
28
|
+
if (this.configs.baseUrl) {
|
|
29
|
+
return this.configs.baseUrl;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return 'https://api.picaos.com';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async create(payload?: {
|
|
36
|
+
/**
|
|
37
|
+
* Unique identifier for the token.
|
|
38
|
+
* @remarks It is recommended to avoid using spaces and colons in this field as it may lead to unexpected behavior in some systems.
|
|
39
|
+
*/
|
|
40
|
+
identity?: string;
|
|
41
|
+
identityType?: "user" | "team" | "organization" | "project";
|
|
42
|
+
/** @deprecated Use 'identity' instead */
|
|
43
|
+
group?: string;
|
|
44
|
+
/** @deprecated */
|
|
45
|
+
label?: string;
|
|
46
|
+
}) {
|
|
47
|
+
const secret = this._clientInfo.secret;
|
|
48
|
+
const url = this._url;
|
|
49
|
+
const headers = getHeaders(secret);
|
|
50
|
+
|
|
51
|
+
const result = await createEventLinkTokenApi(headers, url, payload);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|