@dropsh/plugin-oauth2 0.0.2 → 0.1.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dropsh/plugin-oauth2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/plugins/oauth2/src/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"typescript": "^5.6.0",
|
|
21
21
|
"vitest": "^4.1.8",
|
|
22
|
-
"dropsh": "0.
|
|
22
|
+
"dropsh": "0.1.1"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc",
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { AuthError, createHttpClient, HttpError } from "dropsh/plugin";
|
|
2
|
-
import { readToken, writeToken } from "./token-store.js";
|
|
3
|
-
export function createOAuth2AuthCodeAuth(cfg, deps = {}) {
|
|
4
|
-
const now = deps.now ?? Date.now;
|
|
5
|
-
const http = deps.http ?? createHttpClient();
|
|
6
|
-
// Derive base URL for token storage key
|
|
7
|
-
const baseUrl = cfg.token_url.replace(/\/oauth\/token$/, "");
|
|
8
|
-
return {
|
|
9
|
-
async apply(req) {
|
|
10
|
-
const stored = await readToken(baseUrl, deps.tokenDir);
|
|
11
|
-
if (!stored) {
|
|
12
|
-
throw new AuthError("Session expired. Run 'dropsh login' to authenticate.");
|
|
13
|
-
}
|
|
14
|
-
if (stored.expires_at - 30_000 > now()) {
|
|
15
|
-
return {
|
|
16
|
-
...req,
|
|
17
|
-
headers: { ...(req.headers ?? {}), Authorization: `Bearer ${stored.access_token}` },
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
if (!stored.refresh_token) {
|
|
21
|
-
throw new AuthError("Session expired. Run 'dropsh login' to authenticate.");
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const params = new URLSearchParams({
|
|
25
|
-
grant_type: "refresh_token",
|
|
26
|
-
refresh_token: stored.refresh_token,
|
|
27
|
-
client_id: cfg.client_id,
|
|
28
|
-
});
|
|
29
|
-
const res = await http.send({
|
|
30
|
-
method: "POST",
|
|
31
|
-
url: cfg.token_url,
|
|
32
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
33
|
-
body: params.toString(),
|
|
34
|
-
});
|
|
35
|
-
const body = JSON.parse(res.body);
|
|
36
|
-
if (typeof body.access_token !== "string") {
|
|
37
|
-
throw new AuthError("Token endpoint returned no access_token");
|
|
38
|
-
}
|
|
39
|
-
const ttlSec = typeof body.expires_in === "number" ? body.expires_in : 3600;
|
|
40
|
-
const refreshed = {
|
|
41
|
-
access_token: body.access_token,
|
|
42
|
-
refresh_token: typeof body.refresh_token === "string" ? body.refresh_token : stored.refresh_token,
|
|
43
|
-
expires_at: now() + ttlSec * 1000 - 5000,
|
|
44
|
-
};
|
|
45
|
-
await writeToken(baseUrl, refreshed, deps.tokenDir);
|
|
46
|
-
return {
|
|
47
|
-
...req,
|
|
48
|
-
headers: { ...(req.headers ?? {}), Authorization: `Bearer ${refreshed.access_token}` },
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
if (err instanceof AuthError)
|
|
53
|
-
throw err;
|
|
54
|
-
if (err instanceof HttpError) {
|
|
55
|
-
throw new AuthError("Session expired. Run 'dropsh login' to authenticate.", {
|
|
56
|
-
status: err.status,
|
|
57
|
-
body: err.body,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
throw new AuthError("Session expired. Run 'dropsh login' to authenticate.");
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { AuthError, createHttpClient, HttpError } from "dropsh/plugin";
|
|
2
|
-
export function createOAuth2Auth(cfg, deps = {}) {
|
|
3
|
-
const now = deps.now ?? Date.now;
|
|
4
|
-
const http = deps.http ?? createHttpClient();
|
|
5
|
-
const params = new URLSearchParams({
|
|
6
|
-
client_id: cfg.client_id,
|
|
7
|
-
client_secret: cfg.client_secret,
|
|
8
|
-
});
|
|
9
|
-
if (cfg.type === "oauth2_password") {
|
|
10
|
-
params.set("grant_type", "password");
|
|
11
|
-
params.set("username", cfg.username);
|
|
12
|
-
params.set("password", cfg.password);
|
|
13
|
-
if (cfg.scope)
|
|
14
|
-
params.set("scope", cfg.scope);
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
params.set("grant_type", "client_credentials");
|
|
18
|
-
if (cfg.scope)
|
|
19
|
-
params.set("scope", cfg.scope);
|
|
20
|
-
}
|
|
21
|
-
let cached = null;
|
|
22
|
-
async function fetchToken() {
|
|
23
|
-
try {
|
|
24
|
-
const res = await http.send({
|
|
25
|
-
method: "POST",
|
|
26
|
-
url: cfg.token_url,
|
|
27
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
28
|
-
body: params.toString(),
|
|
29
|
-
});
|
|
30
|
-
const body = JSON.parse(res.body);
|
|
31
|
-
if (typeof body.access_token !== "string") {
|
|
32
|
-
throw new AuthError("Token endpoint returned no access_token");
|
|
33
|
-
}
|
|
34
|
-
const ttlSec = typeof body.expires_in === "number" ? body.expires_in : 3600;
|
|
35
|
-
return { token: body.access_token, expiresAt: now() + ttlSec * 1000 - 5000 };
|
|
36
|
-
}
|
|
37
|
-
catch (err) {
|
|
38
|
-
if (err instanceof HttpError) {
|
|
39
|
-
throw new AuthError(`Token request failed: HTTP ${err.status}`, { body: err.body });
|
|
40
|
-
}
|
|
41
|
-
if (err instanceof AuthError)
|
|
42
|
-
throw err;
|
|
43
|
-
throw new AuthError(`Token request failed: ${err.message}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
async apply(req) {
|
|
48
|
-
if (!cached || cached.expiresAt <= now())
|
|
49
|
-
cached = await fetchToken();
|
|
50
|
-
return {
|
|
51
|
-
...req,
|
|
52
|
-
headers: { ...(req.headers ?? {}), Authorization: `Bearer ${cached.token}` },
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
function hostnameFromUrl(baseUrl) {
|
|
5
|
-
return new URL(baseUrl).hostname;
|
|
6
|
-
}
|
|
7
|
-
export function defaultTokenDir() {
|
|
8
|
-
return join(homedir(), ".config", "dropsh");
|
|
9
|
-
}
|
|
10
|
-
export async function readToken(baseUrl, dir) {
|
|
11
|
-
const tokenDir = dir ?? defaultTokenDir();
|
|
12
|
-
const tokenPath = join(tokenDir, `${hostnameFromUrl(baseUrl)}.json`);
|
|
13
|
-
try {
|
|
14
|
-
const raw = await readFile(tokenPath, "utf8");
|
|
15
|
-
return JSON.parse(raw);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
export async function writeToken(baseUrl, token, dir) {
|
|
22
|
-
const tokenDir = dir ?? defaultTokenDir();
|
|
23
|
-
await mkdir(tokenDir, { recursive: true });
|
|
24
|
-
const tokenPath = join(tokenDir, `${hostnameFromUrl(baseUrl)}.json`);
|
|
25
|
-
await writeFile(tokenPath, JSON.stringify(token, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
26
|
-
}
|