@gjermundgaraba/clankerauth-sdk 0.7.1 → 0.7.2
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 +15 -18
- package/dist/resource.d.ts +2 -37
- package/dist/resource.js +12 -2
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/verify.d.ts +5 -1
- package/dist/verify.js +34 -45
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -69,20 +69,17 @@ const routes = Layer.unwrap(
|
|
|
69
69
|
|
|
70
70
|
return Layer.mergeAll(
|
|
71
71
|
notes.discovery.layer,
|
|
72
|
-
Http.layer({ before: notes.authorize }
|
|
72
|
+
Http.layer([app, notes.session], { before: notes.authorize }).pipe(
|
|
73
73
|
Layer.provide(Resource.middleware(notes).layer),
|
|
74
74
|
),
|
|
75
|
-
ActionMcp.layerHttp(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
app,
|
|
85
|
-
).pipe(Layer.provide(Resource.middleware(notes).layer)),
|
|
75
|
+
ActionMcp.layerHttp([app], {
|
|
76
|
+
name: "notes",
|
|
77
|
+
version: "1.0.0",
|
|
78
|
+
path: "/mcp",
|
|
79
|
+
protocols: [McpProtocol.v2026_07_28],
|
|
80
|
+
errors: authenticationErrors,
|
|
81
|
+
before: notes.authorize,
|
|
82
|
+
}).pipe(Layer.provide(Resource.middleware(notes).layer)),
|
|
86
83
|
);
|
|
87
84
|
}),
|
|
88
85
|
).pipe(Layer.provide(FetchHttpClient.layer));
|
|
@@ -94,7 +91,7 @@ The supplied `HttpClient` must not retry credential exchanges or follow redirect
|
|
|
94
91
|
|
|
95
92
|
## Scope enforcement
|
|
96
93
|
|
|
97
|
-
An application names its scopes once and writes no authorization code. Each action declares what it does to the resource, and `resource.authorize` is the surface's pre-handler hook: it runs before every handler on every surface — HTTP, MCP, Toolkit, CLI — so no handler can forget it.
|
|
94
|
+
An application names its scopes once and writes no authorization code. Each action declares what it does to the resource, and `resource.authorize` is the surface's pre-handler hook: it runs after the input is decoded and before every handler on every surface — HTTP, MCP, Toolkit, CLI — so no handler can forget it.
|
|
98
95
|
|
|
99
96
|
```ts
|
|
100
97
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
@@ -114,7 +111,7 @@ const Notes = ActionGroup.make(
|
|
|
114
111
|
const Http = ActionHttp.make({ apiPath: "/api", errors: authenticationErrors }, Notes, Session);
|
|
115
112
|
|
|
116
113
|
// And bound once per surface, so no handler and no group can forget it.
|
|
117
|
-
const routes = Http.layer(
|
|
114
|
+
const routes = Http.layer([Notes.implement(handlers), notes.session], { before: notes.authorize });
|
|
118
115
|
```
|
|
119
116
|
|
|
120
117
|
`scopes.read` is required at verification, so a read needs nothing further. `scopes.write` is what an `access: "write"` action additionally needs; leave it out and every action passes, which is how a single-scope application is expressed. `InsufficientScope` is a 403 naming **only** the missing scope, so a refusal never enumerates the resource's permissions.
|
|
@@ -123,7 +120,7 @@ A forward-auth browser token carries **every** scope the resource defines, becau
|
|
|
123
120
|
|
|
124
121
|
An action reads identity from `CurrentPrincipal`, which contains `subject`, `scopes`, `actor` — either `{ kind: "client", clientId }` or `{ kind: "key", keyId }` — and `expiresAt`: an access token's verified `exp` in epoch milliseconds, so a host bounding a connection to its credential never decodes the token again. It is `undefined` for an API key, which carries no token lifetime and is re-verified on every request; choose your own bound for those.
|
|
125
122
|
|
|
126
|
-
A hook refusal is a plain declared error: effect-actions encodes it with the schema's status and `Cache-Control: no-store`,
|
|
123
|
+
A hook refusal is a plain declared error: effect-actions encodes it with the schema's status and adds no headers of its own; `Cache-Control: no-store` comes from `Resource.middleware`, which wraps every route it authenticates. Challenge headers come from admission — `Resource.middleware` and `admit` — which is what a client onboarding through RFC 9728 reads. No current client reads the header on a scope refusal, because an MCP denial is a tool error.
|
|
127
124
|
|
|
128
125
|
Middleware authenticates the request and the hook authorizes it; neither filters MCP tool discovery.
|
|
129
126
|
|
|
@@ -195,11 +192,11 @@ Schema-tagged errors, all from `@gjermundgaraba/clankerauth-sdk/errors`: `Unauth
|
|
|
195
192
|
|
|
196
193
|
There is one 403. A credential that is valid but lacks a scope is `InsufficientScope`; a credential that is not this resource's at all — the wrong audience, or an API key with no grant here, which the issuer answers `403` for — is `Unauthorized`, because from the resource's side those are the same thing.
|
|
197
194
|
|
|
198
|
-
JWTs are checked against issuer, audience, EdDSA signature, token type, required claims, expiry and scopes. Sender-constrained tokens are rejected. JWKS
|
|
195
|
+
JWTs are checked against issuer, audience, EdDSA signature, token type, required claims, expiry and scopes. Sender-constrained tokens are rejected. One JWKS read answers every verification for a minute, so a key the issuer starts or stops publishing takes effect within that minute; a `kid` the current document does not publish is `Unauthorized` and never triggers a read, so unauthenticated requests cannot become provider traffic. A failed read, or one whose caller gave up, answers `ProviderUnavailable` (503) for five seconds before the next attempt. API keys are checked online on **every request**, so revocation is effective immediately. A resource that accepts only OAuth access tokens sets `apiKeys: false`; key-shaped bearers then fail as `Unauthorized` without contacting the issuer. Admission through a `Resource` has a five-second deadline; the raw `Verifier` has none, so an in-process caller can await a provider call it cannot cancel.
|
|
199
196
|
|
|
200
|
-
The bundled issuer does not configure automatic signing-key rotation. Immediate-use rotation is supported, but tokens signed by a new key
|
|
197
|
+
The bundled issuer does not configure automatic signing-key rotation. Immediate-use rotation is supported, but tokens signed by a new key are rejected until the next JWKS read, up to a minute. Publish-before-use avoids that window; it is not mandatory.
|
|
201
198
|
|
|
202
|
-
The SDK Resource adapter owns authentication error encoding and challenge headers; effect-actions supplies request-scoped identity and
|
|
199
|
+
The SDK Resource adapter owns authentication error encoding and challenge headers; effect-actions supplies request-scoped identity, and its authentication middleware marks every response it answers or wraps `Cache-Control: no-store`; other cache policy is the host's. Defects and interruption are not relabeled as authentication rejection. Named effects supply tracing boundaries; application logging/tracing layers remain caller-owned. No `onFailure` callbacks or hidden runtime.
|
|
203
200
|
|
|
204
201
|
Operational failures retain their underlying `cause` for application-side Effect error handling. `ProviderUnavailable` and `Unauthorized` keep this diagnostic field outside their public schemas; effect-actions serializes only those schemas. Causes may contain sensitive transport or provider details: inspect selectively, never serialize them into responses or log them indiscriminately.
|
|
205
202
|
|
package/dist/resource.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Schema } from "effect";
|
|
2
2
|
import type * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
3
3
|
import * as Authentication from "@gjermundgaraba/effect-actions/Authentication";
|
|
4
|
-
import { ConfigurationError, InsufficientScope
|
|
4
|
+
import { ConfigurationError, InsufficientScope } from "./errors.ts";
|
|
5
5
|
import * as Verifier from "./verify.ts";
|
|
6
6
|
declare const CurrentPrincipal_base: Context.ServiceClass<CurrentPrincipal, "@clankerauth/CurrentPrincipal", Verifier.Principal>;
|
|
7
7
|
export declare class CurrentPrincipal extends CurrentPrincipal_base {
|
|
@@ -46,42 +46,7 @@ export type Admission = {
|
|
|
46
46
|
};
|
|
47
47
|
/** One resource: its verifier, discovery, scope policy and refusals. Acquire once per application. */
|
|
48
48
|
export declare const make: (options: Options) => Effect.Effect<{
|
|
49
|
-
verifier:
|
|
50
|
-
verifyToken: (token: string) => Effect.Effect<{
|
|
51
|
-
subject: string;
|
|
52
|
-
scopes: string[];
|
|
53
|
-
actor: {
|
|
54
|
-
kind: "client";
|
|
55
|
-
clientId: string;
|
|
56
|
-
};
|
|
57
|
-
expiresAt: number;
|
|
58
|
-
} | {
|
|
59
|
-
subject: string;
|
|
60
|
-
scopes: readonly string[];
|
|
61
|
-
actor: {
|
|
62
|
-
kind: "key";
|
|
63
|
-
keyId: string;
|
|
64
|
-
};
|
|
65
|
-
expiresAt: undefined;
|
|
66
|
-
}, InsufficientScope | import("./errors.ts").ProviderUnavailable | RateLimited | Unauthorized, never>;
|
|
67
|
-
verify: (authorization: string | null | undefined) => Effect.Effect<{
|
|
68
|
-
subject: string;
|
|
69
|
-
scopes: string[];
|
|
70
|
-
actor: {
|
|
71
|
-
kind: "client";
|
|
72
|
-
clientId: string;
|
|
73
|
-
};
|
|
74
|
-
expiresAt: number;
|
|
75
|
-
} | {
|
|
76
|
-
subject: string;
|
|
77
|
-
scopes: readonly string[];
|
|
78
|
-
actor: {
|
|
79
|
-
kind: "key";
|
|
80
|
-
keyId: string;
|
|
81
|
-
};
|
|
82
|
-
expiresAt: undefined;
|
|
83
|
-
}, InsufficientScope | import("./errors.ts").ProviderUnavailable | RateLimited | Unauthorized, never>;
|
|
84
|
-
};
|
|
49
|
+
verifier: Verifier.Verifier;
|
|
85
50
|
discovery: {
|
|
86
51
|
layer: import("effect/Layer").Layer<never, never, import("effect/unstable/http/HttpRouter").HttpRouter>;
|
|
87
52
|
metadataUrl: string;
|
package/dist/resource.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Result, Schema, SchemaAST } from "effect";
|
|
2
2
|
import * as Authentication from "@gjermundgaraba/effect-actions/Authentication";
|
|
3
3
|
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
4
|
-
import { authenticationErrors, ConfigurationError, InsufficientScope, RateLimited, Unauthorized, } from "./errors.js";
|
|
4
|
+
import { authenticationErrors, ConfigurationError, InsufficientScope, ProviderUnavailable, RateLimited, Unauthorized, } from "./errors.js";
|
|
5
5
|
import { Session } from "./session.js";
|
|
6
6
|
import * as Verifier from "./verify.js";
|
|
7
7
|
const encoders = new Map(authenticationErrors.map((schema) => [
|
|
@@ -41,12 +41,22 @@ export const make = Effect.fn("Resource.make")(function* (options) {
|
|
|
41
41
|
}),
|
|
42
42
|
catch: () => new ConfigurationError({ message: "Invalid resource metadata configuration" }),
|
|
43
43
|
});
|
|
44
|
-
const
|
|
44
|
+
const unbounded = yield* Verifier.make({
|
|
45
45
|
issuer: options.issuer,
|
|
46
46
|
resource: identifier,
|
|
47
47
|
requiredScopes: [options.scopes.read],
|
|
48
48
|
apiKeys: options.apiKeys,
|
|
49
49
|
});
|
|
50
|
+
// A request must not wait on an issuer that does not answer: the transport is
|
|
51
|
+
// interrupted and the credential refused as unavailable.
|
|
52
|
+
const deadline = (effect) => Effect.timeoutOrElse(effect, {
|
|
53
|
+
duration: "5 seconds",
|
|
54
|
+
orElse: () => Effect.fail(new ProviderUnavailable({ operation: "verify.timeout" })),
|
|
55
|
+
});
|
|
56
|
+
const verifier = {
|
|
57
|
+
verify: (authorization) => deadline(unbounded.verify(authorization)),
|
|
58
|
+
verifyToken: (token) => deadline(unbounded.verifyToken(token)),
|
|
59
|
+
};
|
|
50
60
|
const challengeValue = (error, missing) => discovery.challenge({ error, scope: missing ?? options.scopes.read });
|
|
51
61
|
/**
|
|
52
62
|
* The one header besides the content type that a refusal carries: an RFC 6750
|
package/dist/session.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const Principal: Schema.Struct<{
|
|
|
15
15
|
export type Principal = typeof Principal.Type;
|
|
16
16
|
/**
|
|
17
17
|
* The credential is a transport concern: a process surface has none, so `whoami` is its
|
|
18
|
-
* own group and never an MCP tool. Bind it with `Http.layer(app, resource.session)`.
|
|
18
|
+
* own group and never an MCP tool. Bind it with `Http.layer([app, resource.session])`.
|
|
19
19
|
*/
|
|
20
20
|
export declare const Session: ActionGroup.Group<"session", readonly [Action.Action<"whoami", Schema.$Record<Schema.String, Schema.Never>, Schema.Struct<{
|
|
21
21
|
readonly subject: Schema.NonEmptyString;
|
package/dist/session.js
CHANGED
|
@@ -14,7 +14,7 @@ export const Principal = Schema.Struct({
|
|
|
14
14
|
});
|
|
15
15
|
/**
|
|
16
16
|
* The credential is a transport concern: a process surface has none, so `whoami` is its
|
|
17
|
-
* own group and never an MCP tool. Bind it with `Http.layer(app, resource.session)`.
|
|
17
|
+
* own group and never an MCP tool. Bind it with `Http.layer([app, resource.session])`.
|
|
18
18
|
*/
|
|
19
19
|
export const Session = ActionGroup.make({ name: "session" }, Action.make("whoami", {
|
|
20
20
|
description: "The verified subject, issuer and scopes behind this request's credential.",
|
package/dist/verify.d.ts
CHANGED
|
@@ -31,7 +31,11 @@ export interface Verifier {
|
|
|
31
31
|
readonly verify: (authorization: string | null | undefined) => Effect.Effect<Principal, AuthenticationError>;
|
|
32
32
|
readonly verifyToken: (token: string) => Effect.Effect<Principal, AuthenticationError>;
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Acquire once per resource; transport is selected by the application, not the SDK.
|
|
36
|
+
* Verification runs to completion: a deadline is the request edge's policy, which
|
|
37
|
+
* `Resource` applies, so an in-process caller can await a provider call it cannot cancel.
|
|
38
|
+
*/
|
|
35
39
|
export declare const make: (options: Options) => Effect.Effect<{
|
|
36
40
|
verifyToken: (token: string) => Effect.Effect<{
|
|
37
41
|
subject: string;
|
package/dist/verify.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Clock, Effect, Exit, Schema
|
|
1
|
+
import { Cause, Clock, DateTime, Effect, Exit, Schema } from "effect";
|
|
2
2
|
import { createLocalJWKSet, decodeProtectedHeader, jwtVerify } from "jose";
|
|
3
3
|
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
4
4
|
import { ConfigurationError, InsufficientScope, ProviderUnavailable, RateLimited, Unauthorized, } from "./errors.js";
|
|
@@ -25,10 +25,18 @@ const KeyResponse = Schema.Struct({
|
|
|
25
25
|
ownerId: Schema.NonEmptyString,
|
|
26
26
|
resource: Schema.String,
|
|
27
27
|
scopes: Schema.Array(Schema.NonEmptyString),
|
|
28
|
-
expiresAt: Schema.NullOr(Schema.
|
|
28
|
+
expiresAt: Schema.NullOr(Schema.DateTimeUtcFromString),
|
|
29
29
|
});
|
|
30
30
|
const unauthorized = () => new Unauthorized({ message: "Authentication required" });
|
|
31
|
-
/**
|
|
31
|
+
/** How long a JWKS document answers verification before it is read again. */
|
|
32
|
+
const documentLifetime = "1 minute";
|
|
33
|
+
/** How long a failed read holds off the next one. */
|
|
34
|
+
const failureCooldown = "5 seconds";
|
|
35
|
+
/**
|
|
36
|
+
* Acquire once per resource; transport is selected by the application, not the SDK.
|
|
37
|
+
* Verification runs to completion: a deadline is the request edge's policy, which
|
|
38
|
+
* `Resource` applies, so an in-process caller can await a provider call it cannot cancel.
|
|
39
|
+
*/
|
|
32
40
|
export const make = Effect.fn("Verifier.make")(function* (options) {
|
|
33
41
|
const client = yield* HttpClient.HttpClient;
|
|
34
42
|
const endpoints = yield* Effect.try({
|
|
@@ -38,49 +46,33 @@ export const make = Effect.fn("Verifier.make")(function* (options) {
|
|
|
38
46
|
}),
|
|
39
47
|
catch: () => new ConfigurationError({ message: "Invalid issuer URL" }),
|
|
40
48
|
});
|
|
41
|
-
const
|
|
42
|
-
let refreshAfter = 0;
|
|
43
|
-
let refreshFailure;
|
|
44
|
-
const makeCachedKeys = Effect.cachedWithTTL(Effect.gen(function* () {
|
|
49
|
+
const document = Effect.fn("Verifier.jwks")(function* () {
|
|
45
50
|
const response = yield* execute(client, HttpClientRequest.get(endpoints.jwks));
|
|
46
51
|
if (response.status !== 200)
|
|
47
52
|
return yield* new ProviderUnavailable({
|
|
48
53
|
operation: "jwks.fetch",
|
|
49
54
|
cause: { status: response.status },
|
|
50
55
|
});
|
|
51
|
-
const
|
|
52
|
-
refreshAfter = (yield* Clock.currentTimeMillis) + 30_000;
|
|
53
|
-
refreshFailure = undefined;
|
|
56
|
+
const jwks = yield* HttpClientResponse.schemaBodyJson(Jwks)(response).pipe(Effect.mapError((cause) => new ProviderUnavailable({ operation: "jwks.decode", cause })));
|
|
54
57
|
return createLocalJWKSet({
|
|
55
|
-
keys:
|
|
58
|
+
keys: jwks.keys.map(({ key_ops, ...key }) => key_ops ? { ...key, key_ops: [...key_ops] } : key),
|
|
56
59
|
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
}, Effect.scoped);
|
|
61
|
+
// One read serves every key identifier until it expires, whatever its outcome, so an
|
|
62
|
+
// unknown `kid` in an unauthenticated request never becomes traffic at the issuer.
|
|
63
|
+
const read = yield* Effect.cachedWithTTL(document(), (exit) => Exit.isSuccess(exit) ? documentLifetime : failureCooldown);
|
|
64
|
+
// Replaying that read must not hand a later request the interruption of an earlier
|
|
65
|
+
// one's deadline; within the window the issuer simply was not reached.
|
|
66
|
+
const keys = Effect.catchCause(read, (cause) => Cause.hasInterrupts(cause)
|
|
67
|
+
? Effect.fail(new ProviderUnavailable({ operation: "jwks.refresh" }))
|
|
68
|
+
: Effect.failCause(cause));
|
|
69
|
+
const signingKey = Effect.fn("Verifier.signingKey")(function* (kid) {
|
|
65
70
|
const resolve = yield* keys;
|
|
66
|
-
return yield*
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const now = yield* Clock.currentTimeMillis;
|
|
72
|
-
// Cool down failed or interrupted refreshes too, without discarding still-valid cached keys.
|
|
73
|
-
if (now < refreshAfter)
|
|
74
|
-
return yield* refreshFailure ?? unauthorized();
|
|
75
|
-
refreshAfter = now + 30_000;
|
|
76
|
-
refreshFailure = new ProviderUnavailable({ operation: "jwks.refresh" });
|
|
77
|
-
const next = yield* makeCachedKeys;
|
|
78
|
-
const refreshed = yield* next.pipe(Effect.tapError((error) => Effect.sync(() => {
|
|
79
|
-
refreshFailure = error;
|
|
80
|
-
})));
|
|
81
|
-
keys = next;
|
|
82
|
-
return yield* resolveKey(refreshed, header);
|
|
83
|
-
}))));
|
|
71
|
+
return yield* Effect.tryPromise({
|
|
72
|
+
try: () => resolve({ alg: "EdDSA", kid }),
|
|
73
|
+
// Selecting from fetched keys does no I/O: every failure is the credential's.
|
|
74
|
+
catch: unauthorized,
|
|
75
|
+
});
|
|
84
76
|
});
|
|
85
77
|
const verifyJwt = Effect.fn("Verifier.jwt")(function* (token) {
|
|
86
78
|
const header = yield* Effect.try({
|
|
@@ -91,7 +83,7 @@ export const make = Effect.fn("Verifier.make")(function* (options) {
|
|
|
91
83
|
// tokens before doing provider I/O; JOSE still enforces its algorithm allowlist.
|
|
92
84
|
if (header.alg !== "EdDSA" || header.kid === undefined)
|
|
93
85
|
return yield* unauthorized();
|
|
94
|
-
const key = yield* signingKey(header);
|
|
86
|
+
const key = yield* signingKey(header.kid);
|
|
95
87
|
const now = yield* Clock.currentTimeMillis;
|
|
96
88
|
const { payload } = yield* Effect.tryPromise({
|
|
97
89
|
try: () => jwtVerify(token, key, {
|
|
@@ -131,10 +123,10 @@ export const make = Effect.fn("Verifier.make")(function* (options) {
|
|
|
131
123
|
cause: { status: response.status },
|
|
132
124
|
});
|
|
133
125
|
const body = yield* HttpClientResponse.schemaBodyJson(KeyResponse)(response).pipe(Effect.mapError((cause) => new ProviderUnavailable({ operation: "api-key.response", cause })));
|
|
134
|
-
|
|
135
|
-
if (body.resource !== options.resource || (expiry !== null && !Number.isFinite(expiry)))
|
|
126
|
+
if (body.resource !== options.resource)
|
|
136
127
|
return yield* new ProviderUnavailable({ operation: "api-key.response" });
|
|
137
|
-
if (
|
|
128
|
+
if (body.expiresAt !== null &&
|
|
129
|
+
DateTime.toEpochMillis(body.expiresAt) <= (yield* Clock.currentTimeMillis))
|
|
138
130
|
return yield* unauthorized();
|
|
139
131
|
return {
|
|
140
132
|
subject: body.ownerId,
|
|
@@ -155,10 +147,7 @@ export const make = Effect.fn("Verifier.make")(function* (options) {
|
|
|
155
147
|
if (missing !== undefined)
|
|
156
148
|
return yield* new InsufficientScope({ scope: missing });
|
|
157
149
|
return principal;
|
|
158
|
-
}
|
|
159
|
-
duration: "5 seconds",
|
|
160
|
-
orElse: () => Effect.fail(new ProviderUnavailable({ operation: "verify.timeout" })),
|
|
161
|
-
}));
|
|
150
|
+
});
|
|
162
151
|
return {
|
|
163
152
|
verifyToken,
|
|
164
153
|
verify: Effect.fn("Verifier.verify")(function* (authorization) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjermundgaraba/clankerauth-sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Effect-native Clanker Auth access-token and API-key verification, with optional effect-actions integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"jose": "6.2.12"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@gjermundgaraba/effect-actions": "0.
|
|
42
|
+
"@gjermundgaraba/effect-actions": "0.5.0",
|
|
43
43
|
"@modelcontextprotocol/client": "2.0.0",
|
|
44
44
|
"@types/node": "26.6.2",
|
|
45
45
|
"typescript": "7.0.2",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"vite-plus": "0.3.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@gjermundgaraba/effect-actions": "0.
|
|
50
|
+
"@gjermundgaraba/effect-actions": "0.5.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"@gjermundgaraba/effect-actions": {
|