deevoauth 1.4.5

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/sdk/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # deevo-auth
2
+
3
+ Official SDK for integrating **Deevo Account** OAuth 2.0 authentication into your applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install deevo-auth
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ### 1. Get your credentials
14
+
15
+ Visit the [Deevo Developer Console](https://deevo.tech/developers) to register your application and get your `clientId` and `clientSecret`.
16
+
17
+ ### 2. Configure the SDK
18
+
19
+ ```javascript
20
+ const { DeevoAuth } = require('deevo-auth');
21
+ // or: import { DeevoAuth } from 'deevo-auth';
22
+
23
+ const deevo = new DeevoAuth({
24
+ clientId: 'YOUR_CLIENT_ID',
25
+ clientSecret: 'YOUR_CLIENT_SECRET',
26
+ redirectUri: 'https://yourapp.com/auth/callback',
27
+ });
28
+ ```
29
+
30
+ ### 3. Redirect users to sign in
31
+
32
+ ```javascript
33
+ // Express.js example
34
+ app.get('/auth/login', (req, res) => {
35
+ const loginUrl = deevo.getAuthUrl();
36
+ res.redirect(loginUrl);
37
+ });
38
+ ```
39
+
40
+ ### 4. Handle the callback
41
+
42
+ ```javascript
43
+ app.get('/auth/callback', async (req, res) => {
44
+ try {
45
+ const { accessToken, user } = await deevo.handleCallback(req.query.code);
46
+
47
+ // user = { sub: 'uid', name: 'John', email: 'john@example.com', picture: '...' }
48
+ req.session.user = user;
49
+ req.session.accessToken = accessToken;
50
+
51
+ res.redirect('/dashboard');
52
+ } catch (error) {
53
+ console.error('Auth failed:', error);
54
+ res.redirect('/login?error=auth_failed');
55
+ }
56
+ });
57
+ ```
58
+
59
+ ## API Reference
60
+
61
+ ### `new DeevoAuth(config)`
62
+
63
+ | Parameter | Type | Required | Description |
64
+ |-----------|------|----------|-------------|
65
+ | `clientId` | string | ✅ | OAuth client ID from Developer Console |
66
+ | `clientSecret` | string | ✅ | OAuth client secret (keep server-side!) |
67
+ | `redirectUri` | string | ✅ | Registered callback URL |
68
+ | `authServerUrl` | string | ❌ | Override auth server (default: `https://deevo.tech`) |
69
+ | `scope` | string | ❌ | Scopes (default: `'profile email'`) |
70
+
71
+ ### `deevo.getAuthUrl(options?)`
72
+
73
+ Returns the URL to redirect users to for authentication.
74
+
75
+ ```javascript
76
+ const url = deevo.getAuthUrl({ state: 'csrf-token' });
77
+ // => https://deevo.tech/login?client_id=...&redirect_uri=...
78
+ ```
79
+
80
+ ### `deevo.exchangeCode(code)`
81
+
82
+ Exchanges an authorization code for tokens.
83
+
84
+ ```javascript
85
+ const tokens = await deevo.exchangeCode(code);
86
+ // => { access_token: '...', token_type: 'Bearer', expires_in: 3600 }
87
+ ```
88
+
89
+ ### `deevo.getUserInfo(accessToken)`
90
+
91
+ Fetches the authenticated user's profile.
92
+
93
+ ```javascript
94
+ const user = await deevo.getUserInfo(tokens.access_token);
95
+ // => { sub: 'uid', name: 'John Doe', email: 'john@example.com', picture: '...' }
96
+ ```
97
+
98
+ ### `deevo.handleCallback(code)`
99
+
100
+ Convenience method: exchanges code AND fetches user info in one call.
101
+
102
+ ```javascript
103
+ const { accessToken, user } = await deevo.handleCallback(code);
104
+ ```
105
+
106
+ ### `deevo.verifyToken(accessToken)`
107
+
108
+ Verifies a token and returns user info. Useful for API middleware.
109
+
110
+ ```javascript
111
+ const user = await deevo.verifyToken(req.headers.authorization.split(' ')[1]);
112
+ ```
113
+
114
+ ## Express Middleware
115
+
116
+ Protect your API routes:
117
+
118
+ ```javascript
119
+ const { DeevoAuth, deevoMiddleware } = require('deevo-auth');
120
+
121
+ const deevo = new DeevoAuth({ /* config */ });
122
+
123
+ app.get('/api/profile', deevoMiddleware(deevo), (req, res) => {
124
+ // req.deevoUser contains the authenticated user's profile
125
+ res.json({ user: req.deevoUser });
126
+ });
127
+ ```
128
+
129
+ ## Next.js Integration
130
+
131
+ ```javascript
132
+ // pages/api/auth/login.js
133
+ import { DeevoAuth } from 'deevo-auth';
134
+
135
+ const deevo = new DeevoAuth({
136
+ clientId: process.env.DEEVO_CLIENT_ID,
137
+ clientSecret: process.env.DEEVO_CLIENT_SECRET,
138
+ redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/api/auth/callback`,
139
+ });
140
+
141
+ export default function handler(req, res) {
142
+ res.redirect(deevo.getAuthUrl());
143
+ }
144
+ ```
145
+
146
+ ```javascript
147
+ // pages/api/auth/callback.js
148
+ export default async function handler(req, res) {
149
+ const { code } = req.query;
150
+ const { accessToken, user } = await deevo.handleCallback(code);
151
+
152
+ // Set session cookie, JWT, etc.
153
+ res.redirect('/dashboard');
154
+ }
155
+ ```
156
+
157
+ ## React Component (Client-side)
158
+
159
+ ```jsx
160
+ function LoginButton() {
161
+ const handleLogin = () => {
162
+ window.location.href = '/api/auth/login';
163
+ };
164
+
165
+ return (
166
+ <button onClick={handleLogin}>
167
+ Sign in with Deevo
168
+ </button>
169
+ );
170
+ }
171
+ ```
172
+
173
+ ## Error Handling
174
+
175
+ ```javascript
176
+ const { DeevoAuthError } = require('deevo-auth');
177
+
178
+ try {
179
+ const { user } = await deevo.handleCallback(code);
180
+ } catch (error) {
181
+ if (error instanceof DeevoAuthError) {
182
+ console.error(`Deevo Auth Error [${error.code}]:`, error.message);
183
+ // error.code: 'invalid_grant', 'invalid_client', 'token_exchange_failed', etc.
184
+ // error.statusCode: HTTP status code
185
+ }
186
+ }
187
+ ```
188
+
189
+ ## OAuth Flow Diagram
190
+
191
+ ```
192
+ Your App Deevo Auth Server
193
+ | |
194
+ | 1. Redirect to /login |
195
+ | ---------------------------→ |
196
+ | | 2. User signs in
197
+ | | (Google or Email)
198
+ | 3. Redirect back with code |
199
+ | ←--------------------------- |
200
+ | |
201
+ | 4. POST /api/oauth/token |
202
+ | ---------------------------→ |
203
+ | |
204
+ | 5. Returns access_token |
205
+ | ←--------------------------- |
206
+ | |
207
+ | 6. GET /api/oauth/userinfo |
208
+ | ---------------------------→ |
209
+ | |
210
+ | 7. Returns user profile |
211
+ | ←--------------------------- |
212
+ ```
213
+
214
+ ## License
215
+
216
+ MIT © [Deevo](https://deevo.tech)
package/sdk/build.js ADDED
@@ -0,0 +1,30 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ // Simple build: copy src to dist with CJS and ESM variants
5
+ const src = fs.readFileSync(path.join(__dirname, 'src', 'index.js'), 'utf8');
6
+
7
+ // Create dist directory
8
+ const distDir = path.join(__dirname, 'dist');
9
+ if (!fs.existsSync(distDir)) {
10
+ fs.mkdirSync(distDir, { recursive: true });
11
+ }
12
+
13
+ // CJS version (remove ESM export lines)
14
+ const cjs = src
15
+ .replace(/^export \{.*\};?$/gm, '')
16
+ .replace(/^export default.*$/gm, '');
17
+
18
+ fs.writeFileSync(path.join(distDir, 'index.js'), cjs);
19
+
20
+ // ESM version (remove CJS export lines)
21
+ const esm = src
22
+ .replace(/if \(typeof module.*\n(.*\n)*?.*module\.exports\.default.*\n\}/m, '');
23
+
24
+ fs.writeFileSync(path.join(distDir, 'index.mjs'), esm);
25
+
26
+ // Copy type definitions
27
+ const types = fs.readFileSync(path.join(__dirname, 'src', 'index.d.ts'), 'utf8');
28
+ fs.writeFileSync(path.join(distDir, 'index.d.ts'), types);
29
+
30
+ console.log('✓ Built deevo-auth SDK to dist/');
Binary file
@@ -0,0 +1,69 @@
1
+ export interface DeevoAuthConfig {
2
+ /** Your OAuth client ID from the Deevo Developer Console */
3
+ clientId: string;
4
+ /** Your OAuth client secret (keep server-side only!) */
5
+ clientSecret: string;
6
+ /** The callback URL registered in your Deevo app */
7
+ redirectUri: string;
8
+ /** Override the auth server URL (default: https://deevo.tech) */
9
+ authServerUrl?: string;
10
+ /** Space-separated scopes (default: 'profile email') */
11
+ scope?: string;
12
+ }
13
+
14
+ export interface TokenResponse {
15
+ access_token: string;
16
+ token_type: string;
17
+ expires_in: number;
18
+ scope?: string;
19
+ }
20
+
21
+ export interface DeevoUser {
22
+ /** Unique user identifier */
23
+ sub: string;
24
+ /** User's full name */
25
+ name: string;
26
+ /** User's email address */
27
+ email: string;
28
+ /** URL to user's profile picture */
29
+ picture: string;
30
+ }
31
+
32
+ export interface CallbackResult {
33
+ accessToken: string;
34
+ tokenType: string;
35
+ expiresIn: number;
36
+ user: DeevoUser;
37
+ }
38
+
39
+ export class DeevoAuth {
40
+ constructor(config: DeevoAuthConfig);
41
+
42
+ /** Get the URL to redirect users to for authentication */
43
+ getAuthUrl(options?: { state?: string }): string;
44
+
45
+ /** Exchange an authorization code for tokens */
46
+ exchangeCode(code: string): Promise<TokenResponse>;
47
+
48
+ /** Get the authenticated user's profile information */
49
+ getUserInfo(accessToken: string): Promise<DeevoUser>;
50
+
51
+ /** Complete OAuth flow: exchange code + get user info */
52
+ handleCallback(code: string): Promise<CallbackResult>;
53
+
54
+ /** Verify an access token and get user info */
55
+ verifyToken(accessToken: string): Promise<DeevoUser>;
56
+ }
57
+
58
+ export class DeevoAuthError extends Error {
59
+ code: string;
60
+ statusCode: number;
61
+ constructor(message: string, code: string, statusCode: number);
62
+ }
63
+
64
+ /** Express.js middleware for protecting routes */
65
+ export function deevoMiddleware(
66
+ deevoAuth: DeevoAuth
67
+ ): (req: any, res: any, next: any) => Promise<void>;
68
+
69
+ export default DeevoAuth;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Deevo Auth SDK
3
+ * Official SDK for integrating Deevo Account OAuth 2.0 into your applications.
4
+ *
5
+ * Usage:
6
+ * import { DeevoAuth } from 'deevo-auth';
7
+ *
8
+ * const deevo = new DeevoAuth({
9
+ * clientId: 'YOUR_CLIENT_ID',
10
+ * clientSecret: 'YOUR_CLIENT_SECRET',
11
+ * redirectUri: 'https://yourapp.com/auth/callback',
12
+ * });
13
+ */
14
+
15
+ const DEFAULT_AUTH_URL = 'https://deevo.tech';
16
+
17
+ class DeevoAuth {
18
+ /**
19
+ * Create a new DeevoAuth instance.
20
+ * @param {Object} config
21
+ * @param {string} config.clientId - Your OAuth client ID from the Deevo Developer Console
22
+ * @param {string} config.clientSecret - Your OAuth client secret (keep this server-side only!)
23
+ * @param {string} config.redirectUri - The callback URL registered in your Deevo app
24
+ * @param {string} [config.authServerUrl] - Override the auth server URL (default: https://deevo.tech)
25
+ * @param {string} [config.scope] - Space-separated scopes (default: 'profile email')
26
+ */
27
+ constructor(config) {
28
+ if (!config.clientId) throw new Error('DeevoAuth: clientId is required');
29
+ if (!config.clientSecret) throw new Error('DeevoAuth: clientSecret is required');
30
+ if (!config.redirectUri) throw new Error('DeevoAuth: redirectUri is required');
31
+
32
+ this.clientId = config.clientId;
33
+ this.clientSecret = config.clientSecret;
34
+ this.redirectUri = config.redirectUri;
35
+ this.authServerUrl = (config.authServerUrl || DEFAULT_AUTH_URL).replace(/\/$/, '');
36
+ this.scope = config.scope || 'profile email';
37
+ }
38
+
39
+ /**
40
+ * Get the URL to redirect users to for authentication.
41
+ * Redirect the user's browser to this URL to start the OAuth flow.
42
+ *
43
+ * @param {Object} [options]
44
+ * @param {string} [options.state] - Optional state parameter for CSRF protection
45
+ * @returns {string} The authorization URL
46
+ *
47
+ * @example
48
+ * // Express.js route
49
+ * app.get('/auth/login', (req, res) => {
50
+ * const url = deevo.getAuthUrl({ state: 'random-csrf-token' });
51
+ * res.redirect(url);
52
+ * });
53
+ */
54
+ getAuthUrl(options = {}) {
55
+ const params = new URLSearchParams({
56
+ client_id: this.clientId,
57
+ redirect_uri: this.redirectUri,
58
+ scope: this.scope,
59
+ });
60
+
61
+ if (options.state) {
62
+ params.set('state', options.state);
63
+ }
64
+
65
+ return `${this.authServerUrl}/login?${params.toString()}`;
66
+ }
67
+
68
+ /**
69
+ * Exchange an authorization code for an access token.
70
+ * Call this in your callback route after the user is redirected back.
71
+ *
72
+ * @param {string} code - The authorization code from the callback URL query params
73
+ * @returns {Promise<Object>} Token response with access_token, token_type, expires_in
74
+ *
75
+ * @example
76
+ * // Express.js callback route
77
+ * app.get('/auth/callback', async (req, res) => {
78
+ * const { code } = req.query;
79
+ * const tokens = await deevo.exchangeCode(code);
80
+ * // tokens.access_token is your bearer token
81
+ * });
82
+ */
83
+ async exchangeCode(code) {
84
+ const response = await fetch(`${this.authServerUrl}/api/oauth/token`, {
85
+ method: 'POST',
86
+ headers: { 'Content-Type': 'application/json' },
87
+ body: JSON.stringify({
88
+ grant_type: 'authorization_code',
89
+ code,
90
+ client_id: this.clientId,
91
+ client_secret: this.clientSecret,
92
+ redirect_uri: this.redirectUri,
93
+ }),
94
+ });
95
+
96
+ if (!response.ok) {
97
+ const error = await response.json().catch(() => ({}));
98
+ throw new DeevoAuthError(
99
+ error.message || `Token exchange failed with status ${response.status}`,
100
+ error.error || 'token_exchange_failed',
101
+ response.status
102
+ );
103
+ }
104
+
105
+ return response.json();
106
+ }
107
+
108
+ /**
109
+ * Get the authenticated user's profile information.
110
+ *
111
+ * @param {string} accessToken - The access token from exchangeCode()
112
+ * @returns {Promise<Object>} User profile { sub, name, email, picture }
113
+ *
114
+ * @example
115
+ * const user = await deevo.getUserInfo(tokens.access_token);
116
+ * console.log(user.email, user.name);
117
+ */
118
+ async getUserInfo(accessToken) {
119
+ const response = await fetch(`${this.authServerUrl}/api/oauth/userinfo`, {
120
+ headers: {
121
+ Authorization: `Bearer ${accessToken}`,
122
+ },
123
+ });
124
+
125
+ if (!response.ok) {
126
+ const error = await response.json().catch(() => ({}));
127
+ throw new DeevoAuthError(
128
+ error.message || `UserInfo request failed with status ${response.status}`,
129
+ error.error || 'userinfo_failed',
130
+ response.status
131
+ );
132
+ }
133
+
134
+ return response.json();
135
+ }
136
+
137
+ /**
138
+ * Complete OAuth flow in one call: exchange code and get user info.
139
+ * Convenience method combining exchangeCode() + getUserInfo().
140
+ *
141
+ * @param {string} code - The authorization code from the callback URL
142
+ * @returns {Promise<Object>} { accessToken, tokenType, expiresIn, user }
143
+ *
144
+ * @example
145
+ * app.get('/auth/callback', async (req, res) => {
146
+ * const { accessToken, user } = await deevo.handleCallback(req.query.code);
147
+ * req.session.user = user;
148
+ * req.session.accessToken = accessToken;
149
+ * res.redirect('/dashboard');
150
+ * });
151
+ */
152
+ async handleCallback(code) {
153
+ const tokens = await this.exchangeCode(code);
154
+ const user = await this.getUserInfo(tokens.access_token);
155
+
156
+ return {
157
+ accessToken: tokens.access_token,
158
+ tokenType: tokens.token_type,
159
+ expiresIn: tokens.expires_in,
160
+ user,
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Verify an access token and get user info (useful for API middleware).
166
+ *
167
+ * @param {string} accessToken - The Bearer token to verify
168
+ * @returns {Promise<Object>} User profile if valid
169
+ * @throws {DeevoAuthError} If the token is invalid or expired
170
+ */
171
+ async verifyToken(accessToken) {
172
+ return this.getUserInfo(accessToken);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Express.js middleware for protecting routes with Deevo Auth.
178
+ *
179
+ * @param {DeevoAuth} deevoAuth - A configured DeevoAuth instance
180
+ * @returns {Function} Express middleware
181
+ *
182
+ * @example
183
+ * app.get('/api/protected', deevoMiddleware(deevo), (req, res) => {
184
+ * res.json({ user: req.deevoUser });
185
+ * });
186
+ */
187
+ function deevoMiddleware(deevoAuth) {
188
+ return async (req, res, next) => {
189
+ const authHeader = req.headers.authorization;
190
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
191
+ return res.status(401).json({ error: 'Missing or invalid authorization header' });
192
+ }
193
+
194
+ const token = authHeader.split(' ')[1];
195
+ try {
196
+ const user = await deevoAuth.verifyToken(token);
197
+ req.deevoUser = user;
198
+ next();
199
+ } catch (error) {
200
+ return res.status(401).json({ error: 'Invalid or expired token' });
201
+ }
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Custom error class for Deevo Auth errors.
207
+ */
208
+ class DeevoAuthError extends Error {
209
+ constructor(message, code, statusCode) {
210
+ super(message);
211
+ this.name = 'DeevoAuthError';
212
+ this.code = code;
213
+ this.statusCode = statusCode;
214
+ }
215
+ }
216
+
217
+ // Export for CommonJS
218
+ if (typeof module !== 'undefined' && module.exports) {
219
+ module.exports = { DeevoAuth, deevoMiddleware, DeevoAuthError };
220
+ module.exports.DeevoAuth = DeevoAuth;
221
+ module.exports.deevoMiddleware = deevoMiddleware;
222
+ module.exports.DeevoAuthError = DeevoAuthError;
223
+ module.exports.default = DeevoAuth;
224
+ }
225
+
226
+ // Export for ESM
227
+
228
+