@getstrata/core 0.5.69 → 0.5.71
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/CHANGELOG.md +9 -0
- package/README.md +1 -1
- package/dist/core/auth/oauth/oidcProvider.d.ts +2 -2
- package/dist/core/auth/oauth/providers.d.ts +2 -2
- package/dist/core/auth/oauth/types.d.ts +2 -2
- package/dist/entries/auth/oauth/oidcProvider.js +4 -4
- package/dist/entries/auth/oauth/providers.js +4 -4
- package/dist/entries/openapi/generator.js +18 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.71
|
|
4
|
+
|
|
5
|
+
- OpenAPI treats `POST /auth/login` and `POST /auth/register` as public (no bearer), including when routes are registered under `API_PREFIX`.
|
|
6
|
+
- Route summaries look up `PUBLIC_ROUTE_DESCRIPTIONS` after stripping `API_PREFIX`.
|
|
7
|
+
|
|
8
|
+
## 0.5.70
|
|
9
|
+
|
|
10
|
+
- `OAuthProvider.getAuthorizationUrl` / `exchangeCode` accept an optional `redirectUri` so HTMX login can use `/oauth/:provider/callback` while the API keeps `OAUTH_REDIRECT_URI`.
|
|
11
|
+
|
|
3
12
|
## 0.5.69
|
|
4
13
|
|
|
5
14
|
- `MembershipLookup.updateMemberRole(organizationId, userId, role)` is required. The uninitialized lookup throws until `configureMembershipLookup()` is called. `MembershipService.updateMemberRole` delegates to the adapter.
|
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
82
82
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
83
83
|
|
|
84
84
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
85
|
-
2. Tag a release: `git tag v0.5.
|
|
85
|
+
2. Tag a release: `git tag v0.5.71 && git push origin v0.5.71`
|
|
86
86
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
87
87
|
|
|
88
88
|
Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
|
|
@@ -11,8 +11,8 @@ declare class OidcProvider implements OAuthProvider {
|
|
|
11
11
|
private readonly options;
|
|
12
12
|
readonly name: string;
|
|
13
13
|
constructor(options: OidcProviderOptions);
|
|
14
|
-
getAuthorizationUrl(state: string): string;
|
|
15
|
-
exchangeCode(code: string): Promise<OAuthProfile>;
|
|
14
|
+
getAuthorizationUrl(state: string, redirectUri?: string): string;
|
|
15
|
+
exchangeCode(code: string, redirectUri?: string): Promise<OAuthProfile>;
|
|
16
16
|
}
|
|
17
17
|
export type { OidcProviderOptions };
|
|
18
18
|
export { OidcProvider };
|
|
@@ -8,8 +8,8 @@ declare class GitHubOAuthProvider implements OAuthProvider {
|
|
|
8
8
|
private readonly options;
|
|
9
9
|
readonly name = "github";
|
|
10
10
|
constructor(options: GitHubOAuthOptions);
|
|
11
|
-
getAuthorizationUrl(state: string): string;
|
|
12
|
-
exchangeCode(code: string): Promise<OAuthProfile>;
|
|
11
|
+
getAuthorizationUrl(state: string, redirectUri?: string): string;
|
|
12
|
+
exchangeCode(code: string, redirectUri?: string): Promise<OAuthProfile>;
|
|
13
13
|
}
|
|
14
14
|
declare class MockOAuthProvider implements OAuthProvider {
|
|
15
15
|
private readonly profile;
|
|
@@ -5,7 +5,7 @@ interface OAuthProfile {
|
|
|
5
5
|
}
|
|
6
6
|
interface OAuthProvider {
|
|
7
7
|
readonly name: string;
|
|
8
|
-
getAuthorizationUrl(state: string): string;
|
|
9
|
-
exchangeCode(code: string): Promise<OAuthProfile>;
|
|
8
|
+
getAuthorizationUrl(state: string, redirectUri?: string): string;
|
|
9
|
+
exchangeCode(code: string, redirectUri?: string): Promise<OAuthProfile>;
|
|
10
10
|
}
|
|
11
11
|
export type { OAuthProfile, OAuthProvider };
|
|
@@ -7,24 +7,24 @@ class OidcProvider {
|
|
|
7
7
|
this.options = options;
|
|
8
8
|
this.name = options.name;
|
|
9
9
|
}
|
|
10
|
-
getAuthorizationUrl(state) {
|
|
10
|
+
getAuthorizationUrl(state, redirectUri = this.options.redirectUri) {
|
|
11
11
|
const params = new URLSearchParams({
|
|
12
12
|
client_id: this.options.clientId,
|
|
13
|
-
redirect_uri:
|
|
13
|
+
redirect_uri: redirectUri,
|
|
14
14
|
response_type: "code",
|
|
15
15
|
scope: (this.options.scopes ?? ["openid", "email", "profile"]).join(" "),
|
|
16
16
|
state
|
|
17
17
|
});
|
|
18
18
|
return `${this.options.issuer.replace(/\/$/, "")}/authorize?${params.toString()}`;
|
|
19
19
|
}
|
|
20
|
-
async exchangeCode(code) {
|
|
20
|
+
async exchangeCode(code, redirectUri = this.options.redirectUri) {
|
|
21
21
|
const tokenResponse = await fetch(`${this.options.issuer.replace(/\/$/, "")}/token`, {
|
|
22
22
|
method: "POST",
|
|
23
23
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
24
24
|
body: new URLSearchParams({
|
|
25
25
|
grant_type: "authorization_code",
|
|
26
26
|
code,
|
|
27
|
-
redirect_uri:
|
|
27
|
+
redirect_uri: redirectUri,
|
|
28
28
|
client_id: this.options.clientId,
|
|
29
29
|
client_secret: this.options.clientSecret
|
|
30
30
|
})
|
|
@@ -54,16 +54,16 @@ class GitHubOAuthProvider {
|
|
|
54
54
|
constructor(options) {
|
|
55
55
|
this.options = options;
|
|
56
56
|
}
|
|
57
|
-
getAuthorizationUrl(state) {
|
|
57
|
+
getAuthorizationUrl(state, redirectUri = this.options.redirectUri) {
|
|
58
58
|
const params = new URLSearchParams({
|
|
59
59
|
client_id: this.options.clientId,
|
|
60
|
-
redirect_uri:
|
|
60
|
+
redirect_uri: redirectUri,
|
|
61
61
|
scope: "read:user user:email",
|
|
62
62
|
state
|
|
63
63
|
});
|
|
64
64
|
return `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
65
65
|
}
|
|
66
|
-
async exchangeCode(code) {
|
|
66
|
+
async exchangeCode(code, redirectUri = this.options.redirectUri) {
|
|
67
67
|
const tokenResponse = await fetch("https://github.com/login/oauth/access_token", {
|
|
68
68
|
method: "POST",
|
|
69
69
|
headers: {
|
|
@@ -74,7 +74,7 @@ class GitHubOAuthProvider {
|
|
|
74
74
|
client_id: this.options.clientId,
|
|
75
75
|
client_secret: this.options.clientSecret,
|
|
76
76
|
code,
|
|
77
|
-
redirect_uri:
|
|
77
|
+
redirect_uri: redirectUri
|
|
78
78
|
})
|
|
79
79
|
});
|
|
80
80
|
const tokenBody = await tokenResponse.json();
|
|
@@ -51,6 +51,7 @@ function sdkClientClassName() {
|
|
|
51
51
|
var PUBLIC_ROUTE_DESCRIPTIONS = {
|
|
52
52
|
"GET /auth/me": "Current authenticated user",
|
|
53
53
|
"POST /auth/login": "Login with email and password",
|
|
54
|
+
"POST /auth/register": "Register with name, email, and password",
|
|
54
55
|
"GET /auth/tokens": "List API tokens",
|
|
55
56
|
"POST /auth/tokens": "Create API token",
|
|
56
57
|
"DELETE /auth/tokens/:id": "Revoke API token",
|
|
@@ -83,27 +84,38 @@ var PUBLIC_ROUTE_DESCRIPTIONS = {
|
|
|
83
84
|
function toOpenApiPath(path) {
|
|
84
85
|
return path.replace(/:([A-Za-z_]+)/g, "{$1}");
|
|
85
86
|
}
|
|
87
|
+
function toRelativeApiPath(path) {
|
|
88
|
+
const prefix = apiPrefix();
|
|
89
|
+
if (path === prefix) {
|
|
90
|
+
return "/";
|
|
91
|
+
}
|
|
92
|
+
if (path.startsWith(`${prefix}/`)) {
|
|
93
|
+
return path.slice(prefix.length);
|
|
94
|
+
}
|
|
95
|
+
return path;
|
|
96
|
+
}
|
|
86
97
|
function requiresBearerAuth(path, method) {
|
|
87
|
-
|
|
98
|
+
const relative = toRelativeApiPath(path);
|
|
99
|
+
if (relative.startsWith("/auth/login") || relative.startsWith("/auth/register") || relative.startsWith("/auth/oauth")) {
|
|
88
100
|
return false;
|
|
89
101
|
}
|
|
90
|
-
if (path.startsWith("/scim/") || path.startsWith("/billing/webhooks/")) {
|
|
102
|
+
if (relative.startsWith("/scim/") || relative.startsWith("/billing/webhooks/") || path.startsWith("/scim/") || path.startsWith("/billing/webhooks/")) {
|
|
91
103
|
return false;
|
|
92
104
|
}
|
|
93
|
-
if (["/health", "/ready", "/metrics", "/"].includes(path)) {
|
|
105
|
+
if (["/health", "/ready", "/metrics", "/"].includes(relative) || ["/health", "/ready", "/metrics", "/"].includes(path)) {
|
|
94
106
|
return false;
|
|
95
107
|
}
|
|
96
|
-
if (method === "GET" && ["/organizations", "/projects", "/tasks", "/search"].some((prefix) => path.startsWith(prefix))) {
|
|
108
|
+
if (method === "GET" && ["/organizations", "/projects", "/tasks", "/search"].some((prefix) => relative.startsWith(prefix) || path.startsWith(prefix))) {
|
|
97
109
|
return false;
|
|
98
110
|
}
|
|
99
|
-
return
|
|
111
|
+
return relative.startsWith("/auth/") || ["POST", "PATCH", "PUT", "DELETE"].includes(method);
|
|
100
112
|
}
|
|
101
113
|
function generateOpenApiSpec(routes) {
|
|
102
114
|
const paths = {};
|
|
103
115
|
for (const route of routes) {
|
|
104
116
|
const openApiPath = toOpenApiPath(route.path);
|
|
105
117
|
const method = route.method.toLowerCase();
|
|
106
|
-
const description = PUBLIC_ROUTE_DESCRIPTIONS[`${route.method} ${route.path}`] ?? `${route.method} ${route.path}`;
|
|
118
|
+
const description = PUBLIC_ROUTE_DESCRIPTIONS[`${route.method} ${toRelativeApiPath(route.path)}`] ?? PUBLIC_ROUTE_DESCRIPTIONS[`${route.method} ${route.path}`] ?? `${route.method} ${route.path}`;
|
|
107
119
|
paths[openApiPath] ??= {};
|
|
108
120
|
paths[openApiPath][method] = {
|
|
109
121
|
summary: description,
|