@frejun/oauth 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/README.md +110 -0
- package/dist/cjs/FrejunOAuth.d.ts +54 -0
- package/dist/cjs/FrejunOAuth.d.ts.map +1 -0
- package/dist/cjs/FrejunOAuth.js +223 -0
- package/dist/cjs/FrejunOAuth.js.map +1 -0
- package/dist/cjs/api.d.ts +22 -0
- package/dist/cjs/api.d.ts.map +1 -0
- package/dist/cjs/api.js +93 -0
- package/dist/cjs/api.js.map +1 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/types.d.ts +65 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/FrejunOAuth.d.ts +54 -0
- package/dist/esm/FrejunOAuth.d.ts.map +1 -0
- package/dist/esm/FrejunOAuth.js +186 -0
- package/dist/esm/FrejunOAuth.js.map +1 -0
- package/dist/esm/api.d.ts +22 -0
- package/dist/esm/api.d.ts.map +1 -0
- package/dist/esm/api.js +87 -0
- package/dist/esm/api.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types.d.ts +65 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +10 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @frejun/oauth
|
|
2
|
+
|
|
3
|
+
FreJun OAuth 2.0 SDK for TypeScript / JavaScript. Handles the browser popup authorization flow, token management, and notifies your app of token changes via events.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @frejun/oauth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { FrejunOAuth } from '@frejun/oauth';
|
|
15
|
+
|
|
16
|
+
const oauth = new FrejunOAuth({
|
|
17
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
18
|
+
clientSecret: 'YOUR_CLIENT_SECRET',
|
|
19
|
+
redirectUri: 'https://yourapp.com/callback', // optional
|
|
20
|
+
extraParams: { state: 'random-csrf-token' }, // optional
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Listen for new tokens
|
|
24
|
+
oauth.on('tokens', (data) => {
|
|
25
|
+
console.log('Access token:', data.access_token);
|
|
26
|
+
console.log('Refresh token:', data.refresh_token);
|
|
27
|
+
console.log('Expires in:', data.expires_in);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Listen for refreshed tokens
|
|
31
|
+
oauth.on('tokensRefreshed', (data) => {
|
|
32
|
+
console.log('New access token:', data.access_token);
|
|
33
|
+
console.log('New refresh token:', data.refresh_token);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Listen for errors
|
|
37
|
+
oauth.on('error', (err) => {
|
|
38
|
+
console.error('OAuth error:', err.message);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Open the FreJun consent popup (browser only)
|
|
42
|
+
oauth.openAuthPopup();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### `new FrejunOAuth(config)`
|
|
48
|
+
|
|
49
|
+
| Option | Type | Required | Description |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `clientId` | `string` | Yes | Your FreJun OAuth app client ID |
|
|
52
|
+
| `clientSecret` | `string` | Yes | Your FreJun OAuth app client secret |
|
|
53
|
+
| `redirectUri` | `string` | No | Override the registered redirect URI |
|
|
54
|
+
| `extraParams` | `Record<string, string>` | No | Extra query params (state, username, etc.) |
|
|
55
|
+
|
|
56
|
+
### Methods
|
|
57
|
+
|
|
58
|
+
- **`getAuthorizationUrl()`** — Returns the FreJun authorization URL as a string.
|
|
59
|
+
- **`openAuthPopup()`** — Opens the consent page in a popup, listens for the auth code via `postMessage`, and automatically exchanges it for tokens. Browser only.
|
|
60
|
+
- **`createTokens(code)`** — Exchange an auth code for tokens. Emits `'tokens'`.
|
|
61
|
+
- **`refreshTokens(refreshToken)`** — Refresh an expired access token. Emits `'tokensRefreshed'`.
|
|
62
|
+
- **`verifyToken(token)`** — Check whether an access or refresh token is valid.
|
|
63
|
+
- **`disconnect(refreshToken)`** — Disconnect the OAuth app from the organization. Revokes all tokens for this organization.
|
|
64
|
+
- **`destroy()`** — Clean up all listeners and close any open popup.
|
|
65
|
+
|
|
66
|
+
### Events
|
|
67
|
+
|
|
68
|
+
| Event | Payload | When |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `authCode` | `{ code, email, ...extraParams }` | Auth code received from popup |
|
|
71
|
+
| `tokens` | `CreateTokenResponse` | Tokens created from an auth code |
|
|
72
|
+
| `tokensRefreshed` | `RefreshTokenResponse` | Tokens refreshed |
|
|
73
|
+
| `error` | `Error` | Any error during the flow |
|
|
74
|
+
|
|
75
|
+
#### `on(event, listener)`
|
|
76
|
+
|
|
77
|
+
Subscribe to an event. The listener is called every time the event fires.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
// Called on every token refresh
|
|
81
|
+
oauth.on('tokensRefreshed', (data) => {
|
|
82
|
+
saveTokens(data.access_token, data.refresh_token);
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `once(event, listener)`
|
|
87
|
+
|
|
88
|
+
Subscribe to an event, but the listener is automatically removed after it fires once.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// Only capture the first set of tokens
|
|
92
|
+
oauth.once('tokens', (data) => {
|
|
93
|
+
console.log('Initial tokens received:', data.access_token);
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `off(event, listener)`
|
|
98
|
+
|
|
99
|
+
Remove a previously registered listener.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const handler = (data) => { /* ... */ };
|
|
103
|
+
oauth.on('tokens', handler);
|
|
104
|
+
// Later, unsubscribe
|
|
105
|
+
oauth.off('tokens', handler);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Notes
|
|
109
|
+
|
|
110
|
+
- The authorization code expires in **10 minutes**.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FrejunOAuthConfig, FrejunOAuthEvents, CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse } from './types.js';
|
|
2
|
+
type EventName = keyof FrejunOAuthEvents;
|
|
3
|
+
/**
|
|
4
|
+
* FreJun OAuth 2.0 client.
|
|
5
|
+
*
|
|
6
|
+
* Handles the browser popup authorization flow, token creation / refresh / verification,
|
|
7
|
+
* and exposes an event emitter so consumers can react to token changes.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const oauth = new FrejunOAuth({ clientId: '...', clientSecret: '...' });
|
|
11
|
+
* oauth.on('tokens', (data) => saveTokens(data));
|
|
12
|
+
* oauth.openAuthPopup();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class FrejunOAuth {
|
|
16
|
+
private readonly config;
|
|
17
|
+
private listeners;
|
|
18
|
+
private onceListeners;
|
|
19
|
+
private popup;
|
|
20
|
+
private messageHandler;
|
|
21
|
+
constructor(config: FrejunOAuthConfig);
|
|
22
|
+
/** Build the FreJun authorization URL with all configured query params. */
|
|
23
|
+
getAuthorizationUrl(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Open the FreJun consent page in a popup and listen for the authorization
|
|
26
|
+
* code via `window.postMessage`.
|
|
27
|
+
*
|
|
28
|
+
* **Browser-only** — throws in non-browser environments.
|
|
29
|
+
*/
|
|
30
|
+
openAuthPopup(): void;
|
|
31
|
+
/** Exchange an authorization code for access & refresh tokens. Emits `'tokens'`. */
|
|
32
|
+
createTokens(code: string): Promise<CreateTokenResponse>;
|
|
33
|
+
/** Refresh an expired access token. Emits `'tokensRefreshed'`. */
|
|
34
|
+
refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
|
|
35
|
+
/** Verify whether a token (access or refresh) is still valid. */
|
|
36
|
+
verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Disconnect the OAuth app from the organization.
|
|
39
|
+
* Revokes all refresh tokens for this client and org, and removes the app-org link.
|
|
40
|
+
*/
|
|
41
|
+
disconnect(refreshToken: string): Promise<DisconnectResponse>;
|
|
42
|
+
/** Subscribe to an event. */
|
|
43
|
+
on<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
44
|
+
/** Unsubscribe from an event. */
|
|
45
|
+
off<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
46
|
+
/** Subscribe to an event — listener is automatically removed after the first call. */
|
|
47
|
+
once<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
48
|
+
/** Remove all listeners and close the popup if open. */
|
|
49
|
+
destroy(): void;
|
|
50
|
+
private emit;
|
|
51
|
+
private cleanupPopup;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=FrejunOAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrejunOAuth.d.ts","sourceRoot":"","sources":["../../src/FrejunOAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EAEjB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAMpB,KAAK,SAAS,GAAG,MAAM,iBAAiB,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,cAAc,CAAgD;gBAE1D,MAAM,EAAE,iBAAiB;IAWrC,2EAA2E;IAC3E,mBAAmB,IAAI,MAAM;IAqB7B;;;;;OAKG;IACH,aAAa,IAAI,IAAI;IAkDrB,oFAAoF;IAC9E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU9D,kEAAkE;IAC5D,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAUxE,iEAAiE;IAC3D,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI9D;;;OAGG;IACG,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAYnE,6BAA6B;IAC7B,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAQvE,iCAAiC;IACjC,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAMxE,sFAAsF;IACtF,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAczE,wDAAwD;IACxD,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FrejunOAuth = void 0;
|
|
37
|
+
const api = __importStar(require("./api.js"));
|
|
38
|
+
const AUTHORIZATION_URL = 'https://product.frejun.com/oauth/authorize/';
|
|
39
|
+
const EXPECTED_ORIGIN = 'https://product.frejun.com';
|
|
40
|
+
/**
|
|
41
|
+
* FreJun OAuth 2.0 client.
|
|
42
|
+
*
|
|
43
|
+
* Handles the browser popup authorization flow, token creation / refresh / verification,
|
|
44
|
+
* and exposes an event emitter so consumers can react to token changes.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* const oauth = new FrejunOAuth({ clientId: '...', clientSecret: '...' });
|
|
48
|
+
* oauth.on('tokens', (data) => saveTokens(data));
|
|
49
|
+
* oauth.openAuthPopup();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
class FrejunOAuth {
|
|
53
|
+
constructor(config) {
|
|
54
|
+
this.listeners = new Map();
|
|
55
|
+
this.onceListeners = new Map();
|
|
56
|
+
this.popup = null;
|
|
57
|
+
this.messageHandler = null;
|
|
58
|
+
if (!config.clientId || !config.clientSecret) {
|
|
59
|
+
throw new Error('clientId and clientSecret are required');
|
|
60
|
+
}
|
|
61
|
+
this.config = config;
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Authorization URL
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
/** Build the FreJun authorization URL with all configured query params. */
|
|
67
|
+
getAuthorizationUrl() {
|
|
68
|
+
const url = new URL(AUTHORIZATION_URL);
|
|
69
|
+
url.searchParams.set('client_id', this.config.clientId);
|
|
70
|
+
if (this.config.redirectUri) {
|
|
71
|
+
url.searchParams.set('redirect_uri', this.config.redirectUri);
|
|
72
|
+
}
|
|
73
|
+
if (this.config.extraParams) {
|
|
74
|
+
for (const [key, value] of Object.entries(this.config.extraParams)) {
|
|
75
|
+
url.searchParams.set(key, value);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return url.toString();
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Browser popup flow
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
/**
|
|
84
|
+
* Open the FreJun consent page in a popup and listen for the authorization
|
|
85
|
+
* code via `window.postMessage`.
|
|
86
|
+
*
|
|
87
|
+
* **Browser-only** — throws in non-browser environments.
|
|
88
|
+
*/
|
|
89
|
+
openAuthPopup() {
|
|
90
|
+
if (typeof window === 'undefined') {
|
|
91
|
+
throw new Error('openAuthPopup() is only available in browser environments');
|
|
92
|
+
}
|
|
93
|
+
const authUrl = this.getAuthorizationUrl();
|
|
94
|
+
// Center the popup on screen
|
|
95
|
+
const width = 600;
|
|
96
|
+
const height = 700;
|
|
97
|
+
const left = Math.round((screen.width - width) / 2);
|
|
98
|
+
const top = Math.round((screen.height - height) / 2);
|
|
99
|
+
this.popup = window.open(authUrl, 'frejun-oauth', `width=${width},height=${height},left=${left},top=${top},scrollbars=yes`);
|
|
100
|
+
this.messageHandler = (event) => {
|
|
101
|
+
// Only accept messages from the FreJun consent page
|
|
102
|
+
if (event.origin !== EXPECTED_ORIGIN || !event.data)
|
|
103
|
+
return;
|
|
104
|
+
if (event.data.eventName !== 'oauth-code')
|
|
105
|
+
return;
|
|
106
|
+
const { code, email, ...otherParams } = (event.data.data || {});
|
|
107
|
+
// Clean up popup + listener regardless of outcome
|
|
108
|
+
this.cleanupPopup();
|
|
109
|
+
if (!code) {
|
|
110
|
+
this.emit('error', new Error('OAuth authorization was declined or no code received'));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const authCodeData = { code, email, ...otherParams };
|
|
114
|
+
this.emit('authCode', authCodeData);
|
|
115
|
+
// Automatically exchange the code for tokens
|
|
116
|
+
this.createTokens(code).catch((err) => {
|
|
117
|
+
this.emit('error', err instanceof Error ? err : new Error(String(err)));
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
window.addEventListener('message', this.messageHandler);
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Token operations
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
/** Exchange an authorization code for access & refresh tokens. Emits `'tokens'`. */
|
|
126
|
+
async createTokens(code) {
|
|
127
|
+
const response = await api.createTokens(this.config.clientId, this.config.clientSecret, code);
|
|
128
|
+
this.emit('tokens', response);
|
|
129
|
+
return response;
|
|
130
|
+
}
|
|
131
|
+
/** Refresh an expired access token. Emits `'tokensRefreshed'`. */
|
|
132
|
+
async refreshTokens(refreshToken) {
|
|
133
|
+
const response = await api.refreshTokens(this.config.clientId, this.config.clientSecret, refreshToken);
|
|
134
|
+
this.emit('tokensRefreshed', response);
|
|
135
|
+
return response;
|
|
136
|
+
}
|
|
137
|
+
/** Verify whether a token (access or refresh) is still valid. */
|
|
138
|
+
async verifyToken(token) {
|
|
139
|
+
return api.verifyToken(token);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Disconnect the OAuth app from the organization.
|
|
143
|
+
* Revokes all refresh tokens for this client and org, and removes the app-org link.
|
|
144
|
+
*/
|
|
145
|
+
async disconnect(refreshToken) {
|
|
146
|
+
return api.disconnect(this.config.clientId, this.config.clientSecret, refreshToken);
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Event emitter (isomorphic — no Node `events` dependency)
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
/** Subscribe to an event. */
|
|
152
|
+
on(event, listener) {
|
|
153
|
+
if (!this.listeners.has(event)) {
|
|
154
|
+
this.listeners.set(event, new Set());
|
|
155
|
+
}
|
|
156
|
+
this.listeners.get(event).add(listener);
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
/** Unsubscribe from an event. */
|
|
160
|
+
off(event, listener) {
|
|
161
|
+
var _a, _b;
|
|
162
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(listener);
|
|
163
|
+
(_b = this.onceListeners.get(event)) === null || _b === void 0 ? void 0 : _b.delete(listener);
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
/** Subscribe to an event — listener is automatically removed after the first call. */
|
|
167
|
+
once(event, listener) {
|
|
168
|
+
if (!this.onceListeners.has(event)) {
|
|
169
|
+
this.onceListeners.set(event, new Set());
|
|
170
|
+
}
|
|
171
|
+
this.onceListeners.get(event).add(listener);
|
|
172
|
+
// Also register in the main listeners map so it fires normally
|
|
173
|
+
this.on(event, listener);
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// Lifecycle
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
/** Remove all listeners and close the popup if open. */
|
|
180
|
+
destroy() {
|
|
181
|
+
this.cleanupPopup();
|
|
182
|
+
this.listeners.clear();
|
|
183
|
+
this.onceListeners.clear();
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Internals
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
emit(event, ...args) {
|
|
189
|
+
var _a;
|
|
190
|
+
const listeners = this.listeners.get(event);
|
|
191
|
+
if (!listeners)
|
|
192
|
+
return;
|
|
193
|
+
for (const listener of listeners) {
|
|
194
|
+
try {
|
|
195
|
+
listener(...args);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
// Avoid swallowing errors silently — log to console
|
|
199
|
+
console.error(`Error in "${event}" listener:`, err);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Remove one-shot listeners after firing
|
|
203
|
+
const onceSet = this.onceListeners.get(event);
|
|
204
|
+
if (onceSet) {
|
|
205
|
+
for (const listener of onceSet) {
|
|
206
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(listener);
|
|
207
|
+
}
|
|
208
|
+
onceSet.clear();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
cleanupPopup() {
|
|
212
|
+
if (this.messageHandler && typeof window !== 'undefined') {
|
|
213
|
+
window.removeEventListener('message', this.messageHandler);
|
|
214
|
+
this.messageHandler = null;
|
|
215
|
+
}
|
|
216
|
+
if (this.popup && !this.popup.closed) {
|
|
217
|
+
this.popup.close();
|
|
218
|
+
}
|
|
219
|
+
this.popup = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
exports.FrejunOAuth = FrejunOAuth;
|
|
223
|
+
//# sourceMappingURL=FrejunOAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrejunOAuth.js","sourceRoot":"","sources":["../../src/FrejunOAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Exchange an authorization code for access and refresh tokens.
|
|
4
|
+
* `GET /oauth/token/?code=<code>`
|
|
5
|
+
*/
|
|
6
|
+
export declare function createTokens(clientId: string, clientSecret: string, code: string): Promise<CreateTokenResponse>;
|
|
7
|
+
/**
|
|
8
|
+
* Refresh an expired access token.
|
|
9
|
+
* `POST /oauth/token/refresh/`
|
|
10
|
+
*/
|
|
11
|
+
export declare function refreshTokens(clientId: string, clientSecret: string, refreshToken: string): Promise<RefreshTokenResponse>;
|
|
12
|
+
/**
|
|
13
|
+
* Verify whether an access or refresh token is still valid.
|
|
14
|
+
* `POST /oauth/verify-token/` (no Authorization header required)
|
|
15
|
+
*/
|
|
16
|
+
export declare function verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* Disconnect the OAuth app from the org associated with the refresh token.
|
|
19
|
+
* `POST /oauth/disconnect-oauth-app/`
|
|
20
|
+
*/
|
|
21
|
+
export declare function disconnect(clientId: string, clientSecret: string, refreshToken: string): Promise<DisconnectResponse>;
|
|
22
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AA2BpB;;;GAGG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAQ9B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAO7E;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAW7B"}
|
package/dist/cjs/api.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTokens = createTokens;
|
|
4
|
+
exports.refreshTokens = refreshTokens;
|
|
5
|
+
exports.verifyToken = verifyToken;
|
|
6
|
+
exports.disconnect = disconnect;
|
|
7
|
+
const types_js_1 = require("./types.js");
|
|
8
|
+
const BASE_URL = 'https://api.frejun.com/api/v2';
|
|
9
|
+
/** Base64-encode client credentials for the Authorization header. */
|
|
10
|
+
function encodeCredentials(clientId, clientSecret) {
|
|
11
|
+
const raw = `${clientId}:${clientSecret}`;
|
|
12
|
+
if (typeof btoa === 'function') {
|
|
13
|
+
return btoa(raw);
|
|
14
|
+
}
|
|
15
|
+
// Node.js fallback
|
|
16
|
+
return Buffer.from(raw).toString('base64');
|
|
17
|
+
}
|
|
18
|
+
/** Parse a JSON response body; throw FrejunApiError on non-2xx status. */
|
|
19
|
+
async function handleResponse(response) {
|
|
20
|
+
const body = await response.json().catch(() => null);
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new types_js_1.FrejunApiError(`FreJun API error: ${response.status} ${response.statusText}`, response.status, body);
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Exchange an authorization code for access and refresh tokens.
|
|
28
|
+
* `GET /oauth/token/?code=<code>`
|
|
29
|
+
*/
|
|
30
|
+
async function createTokens(clientId, clientSecret, code) {
|
|
31
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
32
|
+
const url = `${BASE_URL}/oauth/token/?code=${encodeURIComponent(code)}`;
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
headers: { Authorization: `Bearer ${credentials}` },
|
|
36
|
+
});
|
|
37
|
+
return handleResponse(response);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Refresh an expired access token.
|
|
41
|
+
* `POST /oauth/token/refresh/`
|
|
42
|
+
*/
|
|
43
|
+
async function refreshTokens(clientId, clientSecret, refreshToken) {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
46
|
+
const response = await fetch(`${BASE_URL}/oauth/token/refresh/`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: `Bearer ${credentials}`,
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({ refresh: refreshToken }),
|
|
53
|
+
});
|
|
54
|
+
const raw = await handleResponse(response);
|
|
55
|
+
// Normalize `access`/`refresh` → `access_token`/`refresh_token`
|
|
56
|
+
return {
|
|
57
|
+
success: raw.success,
|
|
58
|
+
message: raw.message,
|
|
59
|
+
access_token: ((_a = raw.access) !== null && _a !== void 0 ? _a : raw.access_token),
|
|
60
|
+
refresh_token: ((_b = raw.refresh) !== null && _b !== void 0 ? _b : raw.refresh_token),
|
|
61
|
+
expires_in: raw.expires_in,
|
|
62
|
+
org_identifier: raw.org_identifier,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Verify whether an access or refresh token is still valid.
|
|
67
|
+
* `POST /oauth/verify-token/` (no Authorization header required)
|
|
68
|
+
*/
|
|
69
|
+
async function verifyToken(token) {
|
|
70
|
+
const response = await fetch(`${BASE_URL}/oauth/verify-token/`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
|
73
|
+
body: JSON.stringify({ token }),
|
|
74
|
+
});
|
|
75
|
+
return handleResponse(response);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Disconnect the OAuth app from the org associated with the refresh token.
|
|
79
|
+
* `POST /oauth/disconnect-oauth-app/`
|
|
80
|
+
*/
|
|
81
|
+
async function disconnect(clientId, clientSecret, refreshToken) {
|
|
82
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
83
|
+
const response = await fetch(`${BASE_URL}/oauth/disconnect-oauth-app/`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
Authorization: `Bearer ${credentials}`,
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
},
|
|
89
|
+
body: JSON.stringify({ refresh: refreshToken }),
|
|
90
|
+
});
|
|
91
|
+
return handleResponse(response);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { FrejunOAuth } from './FrejunOAuth.js';
|
|
2
|
+
export type { FrejunOAuthConfig, FrejunOAuthEvents, AuthCodeData, CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse, } from './types.js';
|
|
3
|
+
export { FrejunApiError } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FrejunApiError = exports.FrejunOAuth = void 0;
|
|
4
|
+
var FrejunOAuth_js_1 = require("./FrejunOAuth.js");
|
|
5
|
+
Object.defineProperty(exports, "FrejunOAuth", { enumerable: true, get: function () { return FrejunOAuth_js_1.FrejunOAuth; } });
|
|
6
|
+
var types_js_1 = require("./types.js");
|
|
7
|
+
Object.defineProperty(exports, "FrejunApiError", { enumerable: true, get: function () { return types_js_1.FrejunApiError; } });
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAAtC,6GAAA,WAAW,OAAA;AAYpB,uCAA4C;AAAnC,0GAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Configuration required to initialize the FreJun OAuth client. */
|
|
2
|
+
export interface FrejunOAuthConfig {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
/** Optional override for the redirect URI registered with the app. */
|
|
6
|
+
redirectUri?: string;
|
|
7
|
+
/** Extra query params appended to the authorization URL (e.g. state, username). */
|
|
8
|
+
extraParams?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
/** Response from `GET /oauth/token/` — creating tokens from an auth code. */
|
|
11
|
+
export interface CreateTokenResponse {
|
|
12
|
+
success: boolean;
|
|
13
|
+
message: string;
|
|
14
|
+
access_token: string;
|
|
15
|
+
refresh_token: string;
|
|
16
|
+
expires_in: number;
|
|
17
|
+
token_type: string;
|
|
18
|
+
org_identifier: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Response from `POST /oauth/token/refresh/`.
|
|
22
|
+
* The API returns `access`/`refresh` but the SDK normalizes them to
|
|
23
|
+
* `access_token`/`refresh_token` for consistency with CreateTokenResponse.
|
|
24
|
+
*/
|
|
25
|
+
export interface RefreshTokenResponse {
|
|
26
|
+
success: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
access_token: string;
|
|
29
|
+
refresh_token: string;
|
|
30
|
+
expires_in: number;
|
|
31
|
+
org_identifier: string;
|
|
32
|
+
}
|
|
33
|
+
/** Response from `POST /oauth/verify-token/`. */
|
|
34
|
+
export interface VerifyTokenResponse {
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** Response from `POST /oauth/disconnect-oauth-app/`. */
|
|
38
|
+
export interface DisconnectResponse {
|
|
39
|
+
success: boolean;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
/** Data received via the postMessage / redirect after user authorization. */
|
|
43
|
+
export interface AuthCodeData {
|
|
44
|
+
code: string;
|
|
45
|
+
email: string;
|
|
46
|
+
[key: string]: string;
|
|
47
|
+
}
|
|
48
|
+
/** Typed event map for the FrejunOAuth event emitter. */
|
|
49
|
+
export interface FrejunOAuthEvents {
|
|
50
|
+
/** Fired when an authorization code is received from the popup. */
|
|
51
|
+
authCode: (data: AuthCodeData) => void;
|
|
52
|
+
/** Fired when new access/refresh tokens are created from an auth code. */
|
|
53
|
+
tokens: (data: CreateTokenResponse) => void;
|
|
54
|
+
/** Fired when tokens are refreshed. */
|
|
55
|
+
tokensRefreshed: (data: RefreshTokenResponse) => void;
|
|
56
|
+
/** Fired on any error during the OAuth flow. */
|
|
57
|
+
error: (error: Error) => void;
|
|
58
|
+
}
|
|
59
|
+
/** Error thrown when a FreJun API request fails. */
|
|
60
|
+
export declare class FrejunApiError extends Error {
|
|
61
|
+
readonly statusCode: number;
|
|
62
|
+
readonly responseBody: unknown;
|
|
63
|
+
constructor(message: string, statusCode: number, responseBody: unknown);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,0EAA0E;IAC1E,MAAM,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5C,uCAAuC;IACvC,eAAe,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,gDAAgD;IAChD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,oDAAoD;AACpD,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,YAAY,EAAE,OAAO,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO;CAMvE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FrejunApiError = void 0;
|
|
4
|
+
/** Error thrown when a FreJun API request fails. */
|
|
5
|
+
class FrejunApiError extends Error {
|
|
6
|
+
constructor(message, statusCode, responseBody) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'FrejunApiError';
|
|
9
|
+
this.statusCode = statusCode;
|
|
10
|
+
this.responseBody = responseBody;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.FrejunApiError = FrejunApiError;
|
|
14
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";;;AAiEA,oDAAoD;AACpD,MAAa,cAAe,SAAQ,KAAK;IAIvC,YAAY,OAAe,EAAE,UAAkB,EAAE,YAAqB;QACpE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;CACF;AAVD,wCAUC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FrejunOAuthConfig, FrejunOAuthEvents, CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse } from './types.js';
|
|
2
|
+
type EventName = keyof FrejunOAuthEvents;
|
|
3
|
+
/**
|
|
4
|
+
* FreJun OAuth 2.0 client.
|
|
5
|
+
*
|
|
6
|
+
* Handles the browser popup authorization flow, token creation / refresh / verification,
|
|
7
|
+
* and exposes an event emitter so consumers can react to token changes.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const oauth = new FrejunOAuth({ clientId: '...', clientSecret: '...' });
|
|
11
|
+
* oauth.on('tokens', (data) => saveTokens(data));
|
|
12
|
+
* oauth.openAuthPopup();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class FrejunOAuth {
|
|
16
|
+
private readonly config;
|
|
17
|
+
private listeners;
|
|
18
|
+
private onceListeners;
|
|
19
|
+
private popup;
|
|
20
|
+
private messageHandler;
|
|
21
|
+
constructor(config: FrejunOAuthConfig);
|
|
22
|
+
/** Build the FreJun authorization URL with all configured query params. */
|
|
23
|
+
getAuthorizationUrl(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Open the FreJun consent page in a popup and listen for the authorization
|
|
26
|
+
* code via `window.postMessage`.
|
|
27
|
+
*
|
|
28
|
+
* **Browser-only** — throws in non-browser environments.
|
|
29
|
+
*/
|
|
30
|
+
openAuthPopup(): void;
|
|
31
|
+
/** Exchange an authorization code for access & refresh tokens. Emits `'tokens'`. */
|
|
32
|
+
createTokens(code: string): Promise<CreateTokenResponse>;
|
|
33
|
+
/** Refresh an expired access token. Emits `'tokensRefreshed'`. */
|
|
34
|
+
refreshTokens(refreshToken: string): Promise<RefreshTokenResponse>;
|
|
35
|
+
/** Verify whether a token (access or refresh) is still valid. */
|
|
36
|
+
verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Disconnect the OAuth app from the organization.
|
|
39
|
+
* Revokes all refresh tokens for this client and org, and removes the app-org link.
|
|
40
|
+
*/
|
|
41
|
+
disconnect(refreshToken: string): Promise<DisconnectResponse>;
|
|
42
|
+
/** Subscribe to an event. */
|
|
43
|
+
on<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
44
|
+
/** Unsubscribe from an event. */
|
|
45
|
+
off<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
46
|
+
/** Subscribe to an event — listener is automatically removed after the first call. */
|
|
47
|
+
once<E extends EventName>(event: E, listener: FrejunOAuthEvents[E]): this;
|
|
48
|
+
/** Remove all listeners and close the popup if open. */
|
|
49
|
+
destroy(): void;
|
|
50
|
+
private emit;
|
|
51
|
+
private cleanupPopup;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=FrejunOAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrejunOAuth.d.ts","sourceRoot":"","sources":["../../src/FrejunOAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EAEjB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAMpB,KAAK,SAAS,GAAG,MAAM,iBAAiB,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IACxD,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,cAAc,CAAgD;gBAE1D,MAAM,EAAE,iBAAiB;IAWrC,2EAA2E;IAC3E,mBAAmB,IAAI,MAAM;IAqB7B;;;;;OAKG;IACH,aAAa,IAAI,IAAI;IAkDrB,oFAAoF;IAC9E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU9D,kEAAkE;IAC5D,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAUxE,iEAAiE;IAC3D,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAI9D;;;OAGG;IACG,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAYnE,6BAA6B;IAC7B,EAAE,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAQvE,iCAAiC;IACjC,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAMxE,sFAAsF;IACtF,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,IAAI;IAczE,wDAAwD;IACxD,OAAO,IAAI,IAAI;IAUf,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as api from './api.js';
|
|
2
|
+
const AUTHORIZATION_URL = 'https://product.frejun.com/oauth/authorize/';
|
|
3
|
+
const EXPECTED_ORIGIN = 'https://product.frejun.com';
|
|
4
|
+
/**
|
|
5
|
+
* FreJun OAuth 2.0 client.
|
|
6
|
+
*
|
|
7
|
+
* Handles the browser popup authorization flow, token creation / refresh / verification,
|
|
8
|
+
* and exposes an event emitter so consumers can react to token changes.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const oauth = new FrejunOAuth({ clientId: '...', clientSecret: '...' });
|
|
12
|
+
* oauth.on('tokens', (data) => saveTokens(data));
|
|
13
|
+
* oauth.openAuthPopup();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class FrejunOAuth {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.listeners = new Map();
|
|
19
|
+
this.onceListeners = new Map();
|
|
20
|
+
this.popup = null;
|
|
21
|
+
this.messageHandler = null;
|
|
22
|
+
if (!config.clientId || !config.clientSecret) {
|
|
23
|
+
throw new Error('clientId and clientSecret are required');
|
|
24
|
+
}
|
|
25
|
+
this.config = config;
|
|
26
|
+
}
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Authorization URL
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/** Build the FreJun authorization URL with all configured query params. */
|
|
31
|
+
getAuthorizationUrl() {
|
|
32
|
+
const url = new URL(AUTHORIZATION_URL);
|
|
33
|
+
url.searchParams.set('client_id', this.config.clientId);
|
|
34
|
+
if (this.config.redirectUri) {
|
|
35
|
+
url.searchParams.set('redirect_uri', this.config.redirectUri);
|
|
36
|
+
}
|
|
37
|
+
if (this.config.extraParams) {
|
|
38
|
+
for (const [key, value] of Object.entries(this.config.extraParams)) {
|
|
39
|
+
url.searchParams.set(key, value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return url.toString();
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Browser popup flow
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Open the FreJun consent page in a popup and listen for the authorization
|
|
49
|
+
* code via `window.postMessage`.
|
|
50
|
+
*
|
|
51
|
+
* **Browser-only** — throws in non-browser environments.
|
|
52
|
+
*/
|
|
53
|
+
openAuthPopup() {
|
|
54
|
+
if (typeof window === 'undefined') {
|
|
55
|
+
throw new Error('openAuthPopup() is only available in browser environments');
|
|
56
|
+
}
|
|
57
|
+
const authUrl = this.getAuthorizationUrl();
|
|
58
|
+
// Center the popup on screen
|
|
59
|
+
const width = 600;
|
|
60
|
+
const height = 700;
|
|
61
|
+
const left = Math.round((screen.width - width) / 2);
|
|
62
|
+
const top = Math.round((screen.height - height) / 2);
|
|
63
|
+
this.popup = window.open(authUrl, 'frejun-oauth', `width=${width},height=${height},left=${left},top=${top},scrollbars=yes`);
|
|
64
|
+
this.messageHandler = (event) => {
|
|
65
|
+
// Only accept messages from the FreJun consent page
|
|
66
|
+
if (event.origin !== EXPECTED_ORIGIN || !event.data)
|
|
67
|
+
return;
|
|
68
|
+
if (event.data.eventName !== 'oauth-code')
|
|
69
|
+
return;
|
|
70
|
+
const { code, email, ...otherParams } = (event.data.data || {});
|
|
71
|
+
// Clean up popup + listener regardless of outcome
|
|
72
|
+
this.cleanupPopup();
|
|
73
|
+
if (!code) {
|
|
74
|
+
this.emit('error', new Error('OAuth authorization was declined or no code received'));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const authCodeData = { code, email, ...otherParams };
|
|
78
|
+
this.emit('authCode', authCodeData);
|
|
79
|
+
// Automatically exchange the code for tokens
|
|
80
|
+
this.createTokens(code).catch((err) => {
|
|
81
|
+
this.emit('error', err instanceof Error ? err : new Error(String(err)));
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
window.addEventListener('message', this.messageHandler);
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Token operations
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
/** Exchange an authorization code for access & refresh tokens. Emits `'tokens'`. */
|
|
90
|
+
async createTokens(code) {
|
|
91
|
+
const response = await api.createTokens(this.config.clientId, this.config.clientSecret, code);
|
|
92
|
+
this.emit('tokens', response);
|
|
93
|
+
return response;
|
|
94
|
+
}
|
|
95
|
+
/** Refresh an expired access token. Emits `'tokensRefreshed'`. */
|
|
96
|
+
async refreshTokens(refreshToken) {
|
|
97
|
+
const response = await api.refreshTokens(this.config.clientId, this.config.clientSecret, refreshToken);
|
|
98
|
+
this.emit('tokensRefreshed', response);
|
|
99
|
+
return response;
|
|
100
|
+
}
|
|
101
|
+
/** Verify whether a token (access or refresh) is still valid. */
|
|
102
|
+
async verifyToken(token) {
|
|
103
|
+
return api.verifyToken(token);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Disconnect the OAuth app from the organization.
|
|
107
|
+
* Revokes all refresh tokens for this client and org, and removes the app-org link.
|
|
108
|
+
*/
|
|
109
|
+
async disconnect(refreshToken) {
|
|
110
|
+
return api.disconnect(this.config.clientId, this.config.clientSecret, refreshToken);
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Event emitter (isomorphic — no Node `events` dependency)
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
/** Subscribe to an event. */
|
|
116
|
+
on(event, listener) {
|
|
117
|
+
if (!this.listeners.has(event)) {
|
|
118
|
+
this.listeners.set(event, new Set());
|
|
119
|
+
}
|
|
120
|
+
this.listeners.get(event).add(listener);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
/** Unsubscribe from an event. */
|
|
124
|
+
off(event, listener) {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(listener);
|
|
127
|
+
(_b = this.onceListeners.get(event)) === null || _b === void 0 ? void 0 : _b.delete(listener);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/** Subscribe to an event — listener is automatically removed after the first call. */
|
|
131
|
+
once(event, listener) {
|
|
132
|
+
if (!this.onceListeners.has(event)) {
|
|
133
|
+
this.onceListeners.set(event, new Set());
|
|
134
|
+
}
|
|
135
|
+
this.onceListeners.get(event).add(listener);
|
|
136
|
+
// Also register in the main listeners map so it fires normally
|
|
137
|
+
this.on(event, listener);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Lifecycle
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
/** Remove all listeners and close the popup if open. */
|
|
144
|
+
destroy() {
|
|
145
|
+
this.cleanupPopup();
|
|
146
|
+
this.listeners.clear();
|
|
147
|
+
this.onceListeners.clear();
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Internals
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
emit(event, ...args) {
|
|
153
|
+
var _a;
|
|
154
|
+
const listeners = this.listeners.get(event);
|
|
155
|
+
if (!listeners)
|
|
156
|
+
return;
|
|
157
|
+
for (const listener of listeners) {
|
|
158
|
+
try {
|
|
159
|
+
listener(...args);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
// Avoid swallowing errors silently — log to console
|
|
163
|
+
console.error(`Error in "${event}" listener:`, err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Remove one-shot listeners after firing
|
|
167
|
+
const onceSet = this.onceListeners.get(event);
|
|
168
|
+
if (onceSet) {
|
|
169
|
+
for (const listener of onceSet) {
|
|
170
|
+
(_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.delete(listener);
|
|
171
|
+
}
|
|
172
|
+
onceSet.clear();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
cleanupPopup() {
|
|
176
|
+
if (this.messageHandler && typeof window !== 'undefined') {
|
|
177
|
+
window.removeEventListener('message', this.messageHandler);
|
|
178
|
+
this.messageHandler = null;
|
|
179
|
+
}
|
|
180
|
+
if (this.popup && !this.popup.closed) {
|
|
181
|
+
this.popup.close();
|
|
182
|
+
}
|
|
183
|
+
this.popup = null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=FrejunOAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FrejunOAuth.js","sourceRoot":"","sources":["../../src/FrejunOAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Exchange an authorization code for access and refresh tokens.
|
|
4
|
+
* `GET /oauth/token/?code=<code>`
|
|
5
|
+
*/
|
|
6
|
+
export declare function createTokens(clientId: string, clientSecret: string, code: string): Promise<CreateTokenResponse>;
|
|
7
|
+
/**
|
|
8
|
+
* Refresh an expired access token.
|
|
9
|
+
* `POST /oauth/token/refresh/`
|
|
10
|
+
*/
|
|
11
|
+
export declare function refreshTokens(clientId: string, clientSecret: string, refreshToken: string): Promise<RefreshTokenResponse>;
|
|
12
|
+
/**
|
|
13
|
+
* Verify whether an access or refresh token is still valid.
|
|
14
|
+
* `POST /oauth/verify-token/` (no Authorization header required)
|
|
15
|
+
*/
|
|
16
|
+
export declare function verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* Disconnect the OAuth app from the org associated with the refresh token.
|
|
19
|
+
* `POST /oauth/disconnect-oauth-app/`
|
|
20
|
+
*/
|
|
21
|
+
export declare function disconnect(clientId: string, clientSecret: string, refreshToken: string): Promise<DisconnectResponse>;
|
|
22
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AA2BpB;;;GAGG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAQ9B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAO7E;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAW7B"}
|
package/dist/esm/api.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { FrejunApiError, } from './types.js';
|
|
2
|
+
const BASE_URL = 'https://api.frejun.com/api/v2';
|
|
3
|
+
/** Base64-encode client credentials for the Authorization header. */
|
|
4
|
+
function encodeCredentials(clientId, clientSecret) {
|
|
5
|
+
const raw = `${clientId}:${clientSecret}`;
|
|
6
|
+
if (typeof btoa === 'function') {
|
|
7
|
+
return btoa(raw);
|
|
8
|
+
}
|
|
9
|
+
// Node.js fallback
|
|
10
|
+
return Buffer.from(raw).toString('base64');
|
|
11
|
+
}
|
|
12
|
+
/** Parse a JSON response body; throw FrejunApiError on non-2xx status. */
|
|
13
|
+
async function handleResponse(response) {
|
|
14
|
+
const body = await response.json().catch(() => null);
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
throw new FrejunApiError(`FreJun API error: ${response.status} ${response.statusText}`, response.status, body);
|
|
17
|
+
}
|
|
18
|
+
return body;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Exchange an authorization code for access and refresh tokens.
|
|
22
|
+
* `GET /oauth/token/?code=<code>`
|
|
23
|
+
*/
|
|
24
|
+
export async function createTokens(clientId, clientSecret, code) {
|
|
25
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
26
|
+
const url = `${BASE_URL}/oauth/token/?code=${encodeURIComponent(code)}`;
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
headers: { Authorization: `Bearer ${credentials}` },
|
|
30
|
+
});
|
|
31
|
+
return handleResponse(response);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Refresh an expired access token.
|
|
35
|
+
* `POST /oauth/token/refresh/`
|
|
36
|
+
*/
|
|
37
|
+
export async function refreshTokens(clientId, clientSecret, refreshToken) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
40
|
+
const response = await fetch(`${BASE_URL}/oauth/token/refresh/`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
Authorization: `Bearer ${credentials}`,
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify({ refresh: refreshToken }),
|
|
47
|
+
});
|
|
48
|
+
const raw = await handleResponse(response);
|
|
49
|
+
// Normalize `access`/`refresh` → `access_token`/`refresh_token`
|
|
50
|
+
return {
|
|
51
|
+
success: raw.success,
|
|
52
|
+
message: raw.message,
|
|
53
|
+
access_token: ((_a = raw.access) !== null && _a !== void 0 ? _a : raw.access_token),
|
|
54
|
+
refresh_token: ((_b = raw.refresh) !== null && _b !== void 0 ? _b : raw.refresh_token),
|
|
55
|
+
expires_in: raw.expires_in,
|
|
56
|
+
org_identifier: raw.org_identifier,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify whether an access or refresh token is still valid.
|
|
61
|
+
* `POST /oauth/verify-token/` (no Authorization header required)
|
|
62
|
+
*/
|
|
63
|
+
export async function verifyToken(token) {
|
|
64
|
+
const response = await fetch(`${BASE_URL}/oauth/verify-token/`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json' },
|
|
67
|
+
body: JSON.stringify({ token }),
|
|
68
|
+
});
|
|
69
|
+
return handleResponse(response);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Disconnect the OAuth app from the org associated with the refresh token.
|
|
73
|
+
* `POST /oauth/disconnect-oauth-app/`
|
|
74
|
+
*/
|
|
75
|
+
export async function disconnect(clientId, clientSecret, refreshToken) {
|
|
76
|
+
const credentials = encodeCredentials(clientId, clientSecret);
|
|
77
|
+
const response = await fetch(`${BASE_URL}/oauth/disconnect-oauth-app/`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
Authorization: `Bearer ${credentials}`,
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({ refresh: refreshToken }),
|
|
84
|
+
});
|
|
85
|
+
return handleResponse(response);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { FrejunOAuth } from './FrejunOAuth.js';
|
|
2
|
+
export type { FrejunOAuthConfig, FrejunOAuthEvents, AuthCodeData, CreateTokenResponse, RefreshTokenResponse, VerifyTokenResponse, DisconnectResponse, } from './types.js';
|
|
3
|
+
export { FrejunApiError } from './types.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAY/C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Configuration required to initialize the FreJun OAuth client. */
|
|
2
|
+
export interface FrejunOAuthConfig {
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
/** Optional override for the redirect URI registered with the app. */
|
|
6
|
+
redirectUri?: string;
|
|
7
|
+
/** Extra query params appended to the authorization URL (e.g. state, username). */
|
|
8
|
+
extraParams?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
/** Response from `GET /oauth/token/` — creating tokens from an auth code. */
|
|
11
|
+
export interface CreateTokenResponse {
|
|
12
|
+
success: boolean;
|
|
13
|
+
message: string;
|
|
14
|
+
access_token: string;
|
|
15
|
+
refresh_token: string;
|
|
16
|
+
expires_in: number;
|
|
17
|
+
token_type: string;
|
|
18
|
+
org_identifier: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Response from `POST /oauth/token/refresh/`.
|
|
22
|
+
* The API returns `access`/`refresh` but the SDK normalizes them to
|
|
23
|
+
* `access_token`/`refresh_token` for consistency with CreateTokenResponse.
|
|
24
|
+
*/
|
|
25
|
+
export interface RefreshTokenResponse {
|
|
26
|
+
success: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
access_token: string;
|
|
29
|
+
refresh_token: string;
|
|
30
|
+
expires_in: number;
|
|
31
|
+
org_identifier: string;
|
|
32
|
+
}
|
|
33
|
+
/** Response from `POST /oauth/verify-token/`. */
|
|
34
|
+
export interface VerifyTokenResponse {
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** Response from `POST /oauth/disconnect-oauth-app/`. */
|
|
38
|
+
export interface DisconnectResponse {
|
|
39
|
+
success: boolean;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
/** Data received via the postMessage / redirect after user authorization. */
|
|
43
|
+
export interface AuthCodeData {
|
|
44
|
+
code: string;
|
|
45
|
+
email: string;
|
|
46
|
+
[key: string]: string;
|
|
47
|
+
}
|
|
48
|
+
/** Typed event map for the FrejunOAuth event emitter. */
|
|
49
|
+
export interface FrejunOAuthEvents {
|
|
50
|
+
/** Fired when an authorization code is received from the popup. */
|
|
51
|
+
authCode: (data: AuthCodeData) => void;
|
|
52
|
+
/** Fired when new access/refresh tokens are created from an auth code. */
|
|
53
|
+
tokens: (data: CreateTokenResponse) => void;
|
|
54
|
+
/** Fired when tokens are refreshed. */
|
|
55
|
+
tokensRefreshed: (data: RefreshTokenResponse) => void;
|
|
56
|
+
/** Fired on any error during the OAuth flow. */
|
|
57
|
+
error: (error: Error) => void;
|
|
58
|
+
}
|
|
59
|
+
/** Error thrown when a FreJun API request fails. */
|
|
60
|
+
export declare class FrejunApiError extends Error {
|
|
61
|
+
readonly statusCode: number;
|
|
62
|
+
readonly responseBody: unknown;
|
|
63
|
+
constructor(message: string, statusCode: number, responseBody: unknown);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,yDAAyD;AACzD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,yDAAyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,0EAA0E;IAC1E,MAAM,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5C,uCAAuC;IACvC,eAAe,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,gDAAgD;IAChD,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,oDAAoD;AACpD,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,YAAY,EAAE,OAAO,CAAC;gBAE1B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO;CAMvE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Error thrown when a FreJun API request fails. */
|
|
2
|
+
export class FrejunApiError extends Error {
|
|
3
|
+
constructor(message, statusCode, responseBody) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'FrejunApiError';
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.responseBody = responseBody;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAiEA,oDAAoD;AACpD,MAAM,OAAO,cAAe,SAAQ,KAAK;IAIvC,YAAY,OAAe,EAAE,UAAkB,EAAE,YAAqB;QACpE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@frejun/oauth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FreJun OAuth 2.0 SDK — browser popup flow, token management, and event-driven token notifications",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/cjs/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/cjs/index.d.ts",
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/cjs/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
21
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
22
|
+
"build": "npm run build:cjs && npm run build:esm",
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"frejun",
|
|
28
|
+
"oauth",
|
|
29
|
+
"oauth2",
|
|
30
|
+
"authentication",
|
|
31
|
+
"tokens"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25.5.0",
|
|
36
|
+
"rimraf": "^5.0.0",
|
|
37
|
+
"typescript": "^5.4.0"
|
|
38
|
+
}
|
|
39
|
+
}
|