nocal-auth-sdk 0.1.5 → 0.1.6
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/dist/NocalAuth.js +1 -0
- package/package.json +1 -1
- package/README.md +0 -325
package/dist/NocalAuth.js
CHANGED
|
@@ -112,6 +112,7 @@ export class NocalAuth {
|
|
|
112
112
|
token: rawData.accessToken ?? rawData.token ?? rawData.access_token,
|
|
113
113
|
refreshToken: rawData.refreshToken,
|
|
114
114
|
user: rawData.user,
|
|
115
|
+
state: rawData.state,
|
|
115
116
|
};
|
|
116
117
|
if (!data || Object.keys(data).length === 0) {
|
|
117
118
|
rejectWith("AUTH_DATA_MISSING");
|
package/package.json
CHANGED
package/README.md
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
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.
|