nocal-auth-sdk 0.1.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 +325 -0
- package/dist/NocalAuth.d.ts +37 -0
- package/dist/NocalAuth.js +133 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Nocal Auth SDK
|
|
2
|
+
|
|
3
|
+
A lightweight, secure client-side TypeScript/JavaScript SDK for implementing "Login with Nocal" in third-party applications via popup flow.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Popup-based OAuth flow** - Opens authentication in a centered popup window
|
|
8
|
+
- ✅ **PostMessage protocol** - Secure communication between popup and parent window
|
|
9
|
+
- ✅ **Origin verification** - Validates that messages come from trusted domain
|
|
10
|
+
- ✅ **Memory cleanup** - Automatically removes event listeners and closes popup
|
|
11
|
+
- ✅ **Full TypeScript support** - Fully typed for better DX
|
|
12
|
+
- ✅ **CSRF protection** - Support for state parameter validation
|
|
13
|
+
- ✅ **Zero dependencies** - Uses native browser APIs only
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### From npm (when published):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install nocal-auth-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### From local development:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install /path/to/sdk
|
|
27
|
+
# or
|
|
28
|
+
npm pack
|
|
29
|
+
npm install nocal-auth-sdk-0.1.0.tgz
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Basic Usage (Vanilla JavaScript/TypeScript)
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { NocalAuth } from 'nocal-auth-sdk';
|
|
38
|
+
|
|
39
|
+
// Initialize the SDK
|
|
40
|
+
const nocalAuth = new NocalAuth({
|
|
41
|
+
clientId: 'your_client_id',
|
|
42
|
+
redirectUri: 'https://yourapp.example.com/auth/callback',
|
|
43
|
+
scope: 'openid profile email',
|
|
44
|
+
state: 'random_state_value' // CSRF protection
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Trigger login in popup
|
|
48
|
+
async function handleLoginClick() {
|
|
49
|
+
const result = await nocalAuth.loginWithPopup({
|
|
50
|
+
width: 520,
|
|
51
|
+
height: 700
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (result.type === 'success') {
|
|
55
|
+
console.log('✅ Login successful!', result.data);
|
|
56
|
+
// result.data may contain: { token, id_token, code, user, profile, ... }
|
|
57
|
+
// Handle token storage, redirect, etc.
|
|
58
|
+
} else if (result.type === 'error') {
|
|
59
|
+
console.error('❌ Authentication failed:', result.error);
|
|
60
|
+
// Handle error: popup_blocked, unknown_error, etc.
|
|
61
|
+
} else if (result.type === 'closed') {
|
|
62
|
+
console.warn('⚠️ User closed popup before completing auth');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### React Example
|
|
68
|
+
|
|
69
|
+
```jsx
|
|
70
|
+
import { useCallback } from 'react';
|
|
71
|
+
import { NocalAuth } from 'nocal-auth-sdk';
|
|
72
|
+
|
|
73
|
+
export function LoginButton() {
|
|
74
|
+
const handleLogin = useCallback(async () => {
|
|
75
|
+
const sdk = new NocalAuth({
|
|
76
|
+
clientId: process.env.REACT_APP_NOCAL_CLIENT_ID!,
|
|
77
|
+
redirectUri: `${window.location.origin}/auth/callback`,
|
|
78
|
+
scope: 'openid profile email',
|
|
79
|
+
state: generateRandomState() // Use crypto.randomUUID() or similar
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const result = await sdk.loginWithPopup({ width: 520, height: 700 });
|
|
83
|
+
|
|
84
|
+
switch (result.type) {
|
|
85
|
+
case 'success':
|
|
86
|
+
// Store token
|
|
87
|
+
localStorage.setItem('nocal_token', result.data.token);
|
|
88
|
+
// Redirect or update app state
|
|
89
|
+
window.location.href = '/dashboard';
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case 'error':
|
|
93
|
+
alert(`Login failed: ${result.error}`);
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case 'closed':
|
|
97
|
+
console.log('User cancelled login');
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<button onClick={handleLogin} className="btn btn-primary">
|
|
104
|
+
🔐 Login with Nocal
|
|
105
|
+
</button>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function generateRandomState() {
|
|
110
|
+
return crypto.randomUUID?.() || Math.random().toString(36).substr(2);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Vue 3 Composition API Example
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
import { ref } from 'vue';
|
|
119
|
+
import { NocalAuth } from 'nocal-auth-sdk';
|
|
120
|
+
|
|
121
|
+
const isLoading = ref(false);
|
|
122
|
+
const error = ref('');
|
|
123
|
+
|
|
124
|
+
const handleLogin = async () => {
|
|
125
|
+
isLoading.value = true;
|
|
126
|
+
error.value = '';
|
|
127
|
+
|
|
128
|
+
const sdk = new NocalAuth({
|
|
129
|
+
clientId: import.meta.env.VITE_NOCAL_CLIENT_ID,
|
|
130
|
+
redirectUri: `${window.location.origin}/auth/callback`,
|
|
131
|
+
scope: 'openid profile email'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const result = await sdk.loginWithPopup();
|
|
135
|
+
|
|
136
|
+
if (result.type === 'success') {
|
|
137
|
+
// Store token and redirect
|
|
138
|
+
localStorage.setItem('nocal_token', result.data.token);
|
|
139
|
+
window.location.href = '/dashboard';
|
|
140
|
+
} else if (result.type === 'error') {
|
|
141
|
+
error.value = result.error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
isLoading.value = false;
|
|
145
|
+
};
|
|
146
|
+
</script>
|
|
147
|
+
|
|
148
|
+
<template>
|
|
149
|
+
<button @click="handleLogin" :disabled="isLoading">
|
|
150
|
+
{{ isLoading ? 'Logging in...' : '🔐 Login with Nocal' }}
|
|
151
|
+
</button>
|
|
152
|
+
<p v-if="error" class="error">{{ error }}</p>
|
|
153
|
+
</template>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## API Reference
|
|
157
|
+
|
|
158
|
+
### Constructor
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
new NocalAuth(options: NocalAuthOptions)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Options:**
|
|
165
|
+
|
|
166
|
+
| Property | Type | Required | Default | Description |
|
|
167
|
+
|----------|------|----------|---------|-------------|
|
|
168
|
+
| `clientId` | `string` | ✅ | - | OAuth client ID from Nocal Auth |
|
|
169
|
+
| `redirectUri` | `string` | ✅ | - | Callback URL after authentication |
|
|
170
|
+
| `scope` | `string` | ❌ | - | OAuth scopes (e.g., `"openid profile email"`) |
|
|
171
|
+
| `state` | `string` | ❌ | - | CSRF token (you should generate this) |
|
|
172
|
+
| `issuerOrigin` | `string` | ❌ | `https://auth.nocal.dev` | Authorized domain for postMessage |
|
|
173
|
+
| `popupFeatures` | `string` | ❌ | - | Extra window.open features |
|
|
174
|
+
| `extraParams` | `Record<string, string>` | ❌ | - | Additional query parameters |
|
|
175
|
+
|
|
176
|
+
### Methods
|
|
177
|
+
|
|
178
|
+
#### `loginWithPopup(options?: PopupOptions): Promise<PopupResult<T>>`
|
|
179
|
+
|
|
180
|
+
Opens a centered popup for authentication.
|
|
181
|
+
|
|
182
|
+
**PopupOptions:**
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
interface PopupOptions {
|
|
186
|
+
width?: number; // Default: 500
|
|
187
|
+
height?: number; // Default: 700
|
|
188
|
+
popupName?: string; // Default: 'nocal_auth_popup'
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**PopupResult:**
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
type PopupResult<T> =
|
|
196
|
+
| { type: 'success'; data: T } // Authentication succeeded
|
|
197
|
+
| { type: 'error'; error: string } // Error occurred
|
|
198
|
+
| { type: 'closed' } // User closed popup
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Returns:** `Promise<PopupResult<Record<string, any>>>`
|
|
202
|
+
|
|
203
|
+
The `data` object typically contains:
|
|
204
|
+
- `token` - Access token
|
|
205
|
+
- `id_token` - OpenID Connect token (if `openid` in scope)
|
|
206
|
+
- `code` - Authorization code (if using code flow)
|
|
207
|
+
- `user` or `profile` - User information
|
|
208
|
+
- Other fields based on your server configuration
|
|
209
|
+
|
|
210
|
+
## Server-Side Requirements
|
|
211
|
+
|
|
212
|
+
Your Nocal Auth server must:
|
|
213
|
+
|
|
214
|
+
1. Accept the authorize request at `/authorize?client_id=...&redirect_uri=...`
|
|
215
|
+
2. After successful authentication, post a message back to the popup's opener:
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// In the popup (auth.nocal.dev), after auth completes:
|
|
219
|
+
window.opener.postMessage(
|
|
220
|
+
{
|
|
221
|
+
type: 'nocal_auth',
|
|
222
|
+
success: true,
|
|
223
|
+
data: {
|
|
224
|
+
token: 'access_token_value',
|
|
225
|
+
id_token: 'id_token_value', // optional
|
|
226
|
+
user: { id: '123', email: 'user@example.com' }
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
'https://yourapp.example.com' // client origin
|
|
230
|
+
);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Or on error:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
window.opener.postMessage(
|
|
237
|
+
{
|
|
238
|
+
type: 'nocal_auth',
|
|
239
|
+
success: false,
|
|
240
|
+
error: 'invalid_grant' // or other error code
|
|
241
|
+
},
|
|
242
|
+
'https://yourapp.example.com'
|
|
243
|
+
);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
3. Ensure the origin in `postMessage` matches your client app's origin for browser security
|
|
247
|
+
|
|
248
|
+
## Security Considerations
|
|
249
|
+
|
|
250
|
+
### 1. **Origin Verification** ✅
|
|
251
|
+
- SDK automatically verifies `event.origin === issuerOrigin` before accepting messages
|
|
252
|
+
- Prevents clickjacking and XSS attacks from unauthorized domains
|
|
253
|
+
|
|
254
|
+
### 2. **State Parameter** 🔐
|
|
255
|
+
- Always generate a random `state` value client-side
|
|
256
|
+
- Pass it to the SDK constructor
|
|
257
|
+
- Server should validate it matches before returning auth result
|
|
258
|
+
- Example: `crypto.randomUUID()` or `Math.random().toString(36)`
|
|
259
|
+
|
|
260
|
+
### 3. **HTTPS Only** 🔒
|
|
261
|
+
- Both client app and Nocal server must use HTTPS in production
|
|
262
|
+
- Prevents man-in-the-middle attacks
|
|
263
|
+
|
|
264
|
+
### 4. **Token Storage** 💾
|
|
265
|
+
- Store tokens securely:
|
|
266
|
+
- **Recommended:** Session/memory (least persistent, most secure)
|
|
267
|
+
- **Alternative:** localStorage with HttpOnly cookie for sensitive data
|
|
268
|
+
- **Avoid:** localStorage for sensitive tokens without additional encryption
|
|
269
|
+
|
|
270
|
+
### 5. **PKCE for SPA** (Future Enhancement)
|
|
271
|
+
- For maximum security, implement PKCE (RFC 7636)
|
|
272
|
+
- Protects against authorization code interception
|
|
273
|
+
|
|
274
|
+
## Building from Source
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Install dependencies
|
|
278
|
+
npm install
|
|
279
|
+
|
|
280
|
+
# Build TypeScript to dist/
|
|
281
|
+
npm run build
|
|
282
|
+
|
|
283
|
+
# Output structure:
|
|
284
|
+
# dist/
|
|
285
|
+
# ├── index.d.ts (TypeScript declarations)
|
|
286
|
+
# ├── index.esm.js (ES modules)
|
|
287
|
+
# └── index.cjs.js (CommonJS)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Publishing to npm
|
|
291
|
+
|
|
292
|
+
1. Update version in `package.json`
|
|
293
|
+
2. Build: `npm run build`
|
|
294
|
+
3. Test locally: `npm pack`
|
|
295
|
+
4. Publish: `npm publish`
|
|
296
|
+
|
|
297
|
+
For private registry:
|
|
298
|
+
```bash
|
|
299
|
+
npm publish --registry=https://your-private-registry.com
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Troubleshooting
|
|
303
|
+
|
|
304
|
+
### Popup is blocked
|
|
305
|
+
- Ensure login is triggered by direct user action (click event)
|
|
306
|
+
- Some browsers block popups from async operations
|
|
307
|
+
- Check browser console for popup blocker warnings
|
|
308
|
+
|
|
309
|
+
### Message not received
|
|
310
|
+
- Verify `issuerOrigin` matches exactly (protocol, domain, port)
|
|
311
|
+
- Check Network tab in DevTools for postMessage calls
|
|
312
|
+
- Ensure popup is not on cross-origin without proper headers
|
|
313
|
+
|
|
314
|
+
### Token not persisting
|
|
315
|
+
- Check localStorage/sessionStorage permissions
|
|
316
|
+
- Verify HTTPS in production
|
|
317
|
+
- Check cookie policies and SameSite attributes
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
MIT
|
|
322
|
+
|
|
323
|
+
## Support
|
|
324
|
+
|
|
325
|
+
For issues or questions, open an issue on the Nocal Auth repository.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type PopupResult<T = Record<string, unknown>> = {
|
|
2
|
+
type: "success";
|
|
3
|
+
data: T;
|
|
4
|
+
} | {
|
|
5
|
+
type: "error";
|
|
6
|
+
error: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "closed";
|
|
9
|
+
};
|
|
10
|
+
export interface NocalAuthOptions {
|
|
11
|
+
clientId: string;
|
|
12
|
+
redirectUri: string;
|
|
13
|
+
scope?: string;
|
|
14
|
+
state?: string;
|
|
15
|
+
issuerOrigin?: string;
|
|
16
|
+
responseType?: "code" | "token";
|
|
17
|
+
popupFeatures?: string;
|
|
18
|
+
extraParams?: Record<string, string | number | boolean | undefined>;
|
|
19
|
+
}
|
|
20
|
+
export interface PopupOptions {
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
popupName?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class NocalAuth {
|
|
26
|
+
private readonly clientId;
|
|
27
|
+
private readonly redirectUri;
|
|
28
|
+
private readonly scope?;
|
|
29
|
+
private readonly state?;
|
|
30
|
+
private readonly issuerOrigin;
|
|
31
|
+
private readonly responseType;
|
|
32
|
+
private readonly popupFeatures?;
|
|
33
|
+
private readonly extraParams?;
|
|
34
|
+
constructor(options: NocalAuthOptions);
|
|
35
|
+
private getAuthorizeUrl;
|
|
36
|
+
loginWithPopup(popupOptions?: PopupOptions): Promise<Record<string, unknown>>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
function buildQuery(params) {
|
|
2
|
+
const qs = Object.entries(params)
|
|
3
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== "")
|
|
4
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
|
5
|
+
.join("&");
|
|
6
|
+
return qs ? `?${qs}` : "";
|
|
7
|
+
}
|
|
8
|
+
export class NocalAuth {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
if (!options.clientId) {
|
|
11
|
+
throw new Error("NocalAuth requires a clientId");
|
|
12
|
+
}
|
|
13
|
+
if (!options.redirectUri) {
|
|
14
|
+
throw new Error("NocalAuth requires a redirectUri");
|
|
15
|
+
}
|
|
16
|
+
this.clientId = options.clientId;
|
|
17
|
+
this.redirectUri = options.redirectUri;
|
|
18
|
+
this.scope = options.scope;
|
|
19
|
+
this.state = options.state;
|
|
20
|
+
this.issuerOrigin = (options.issuerOrigin ?? "https://auth.nocal.vn").replace(/\/$/, "");
|
|
21
|
+
this.responseType = options.responseType ?? "code";
|
|
22
|
+
this.popupFeatures = options.popupFeatures;
|
|
23
|
+
this.extraParams = options.extraParams;
|
|
24
|
+
}
|
|
25
|
+
getAuthorizeUrl() {
|
|
26
|
+
const baseUrl = `${this.issuerOrigin}/authorize`;
|
|
27
|
+
const query = buildQuery({
|
|
28
|
+
client_id: this.clientId,
|
|
29
|
+
redirect_uri: this.redirectUri,
|
|
30
|
+
response_type: this.responseType,
|
|
31
|
+
scope: this.scope,
|
|
32
|
+
state: this.state,
|
|
33
|
+
...this.extraParams,
|
|
34
|
+
});
|
|
35
|
+
return `${baseUrl}${query}`;
|
|
36
|
+
}
|
|
37
|
+
async loginWithPopup(popupOptions = {}) {
|
|
38
|
+
if (typeof window === "undefined") {
|
|
39
|
+
throw new Error("loginWithPopup() can only be used in a browser environment");
|
|
40
|
+
}
|
|
41
|
+
const width = popupOptions.width ?? 500;
|
|
42
|
+
const height = popupOptions.height ?? 700;
|
|
43
|
+
const popupName = popupOptions.popupName ?? "nocal_auth_popup";
|
|
44
|
+
const dualScreenLeft = window.screenLeft ?? window.screenX ?? 0;
|
|
45
|
+
const dualScreenTop = window.screenTop ?? window.screenY ?? 0;
|
|
46
|
+
const screenWidth = window.innerWidth ??
|
|
47
|
+
document.documentElement.clientWidth ??
|
|
48
|
+
window.screen.width ??
|
|
49
|
+
1280;
|
|
50
|
+
const screenHeight = window.innerHeight ??
|
|
51
|
+
document.documentElement.clientHeight ??
|
|
52
|
+
window.screen.height ??
|
|
53
|
+
720;
|
|
54
|
+
const left = Math.round(dualScreenLeft + (screenWidth - width) / 2);
|
|
55
|
+
const top = Math.round(dualScreenTop + (screenHeight - height) / 2);
|
|
56
|
+
const features = [
|
|
57
|
+
`width=${width}`,
|
|
58
|
+
`height=${height}`,
|
|
59
|
+
`left=${left}`,
|
|
60
|
+
`top=${top}`,
|
|
61
|
+
"resizable=yes",
|
|
62
|
+
"scrollbars=yes",
|
|
63
|
+
this.popupFeatures ?? "",
|
|
64
|
+
]
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join(",");
|
|
67
|
+
const popup = window.open(this.getAuthorizeUrl(), popupName, features);
|
|
68
|
+
if (!popup) {
|
|
69
|
+
throw new Error("POPUP_BLOCKED");
|
|
70
|
+
}
|
|
71
|
+
popup.focus?.();
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
let settled = false;
|
|
74
|
+
let checkInterval = undefined;
|
|
75
|
+
const cleanup = () => {
|
|
76
|
+
settled = true;
|
|
77
|
+
window.removeEventListener("message", handleMessage);
|
|
78
|
+
if (checkInterval) {
|
|
79
|
+
clearInterval(checkInterval);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const rejectWith = (message) => {
|
|
83
|
+
if (settled)
|
|
84
|
+
return;
|
|
85
|
+
cleanup();
|
|
86
|
+
reject(new Error(message));
|
|
87
|
+
};
|
|
88
|
+
const handleMessage = (event) => {
|
|
89
|
+
if (settled)
|
|
90
|
+
return;
|
|
91
|
+
if (event.origin !== this.issuerOrigin) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const payload = event.data;
|
|
95
|
+
if (!payload || typeof payload !== "object") {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const isValidPayload = payload.type === "nocal_auth" ||
|
|
99
|
+
payload.source === "nocal_auth" ||
|
|
100
|
+
Boolean(payload.code ||
|
|
101
|
+
payload.token ||
|
|
102
|
+
payload.access_token ||
|
|
103
|
+
payload.id_token ||
|
|
104
|
+
payload.error);
|
|
105
|
+
if (!isValidPayload) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
cleanup();
|
|
109
|
+
if (payload.success === false || payload.error) {
|
|
110
|
+
rejectWith(typeof payload.error === "string" ? payload.error : "AUTH_FAILED");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const data = payload.data ?? {
|
|
114
|
+
code: payload.code,
|
|
115
|
+
token: payload.token ?? payload.access_token,
|
|
116
|
+
id_token: payload.id_token,
|
|
117
|
+
state: payload.state,
|
|
118
|
+
};
|
|
119
|
+
if (!data || Object.keys(data).length === 0) {
|
|
120
|
+
rejectWith("AUTH_DATA_MISSING");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
resolve(data);
|
|
124
|
+
};
|
|
125
|
+
window.addEventListener("message", handleMessage, false);
|
|
126
|
+
checkInterval = window.setInterval(() => {
|
|
127
|
+
if (popup.closed) {
|
|
128
|
+
rejectWith("POPUP_CLOSED");
|
|
129
|
+
}
|
|
130
|
+
}, 500);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NocalAuth } from './NocalAuth';
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nocal-auth-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Nocal Auth - client-side SDK for Login with Nocal (popup flow)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"prepare": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"nocal",
|
|
25
|
+
"oauth",
|
|
26
|
+
"popup",
|
|
27
|
+
"auth",
|
|
28
|
+
"supabase"
|
|
29
|
+
],
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|