@almadar/integrations 2.37.0 → 2.39.0
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/index.d.ts +44 -4
- package/dist/index.js +100 -33
- package/dist/index.js.map +1 -1
- package/dist/mocks/index.js +2 -2
- package/dist/mocks/index.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +99 -32
- package/dist/runtime/index.js.map +1 -1
- package/dist/{store-RNvxZO-q.d.ts → store-DG5OGiuQ.d.ts} +8 -2
- package/package.json +5 -5
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { g as IntegrationParams, I as IntegrationCallContext, d as IntegrationFactory } from '../BaseIntegration-CWdas-N_.js';
|
|
2
|
-
import { o as IntegrationName, m as IntegrationActionName, n as IntegrationContracts, e as CredentialStore } from '../store-
|
|
2
|
+
import { o as IntegrationName, m as IntegrationActionName, n as IntegrationContracts, e as CredentialStore } from '../store-DG5OGiuQ.js';
|
|
3
3
|
import '@almadar/core';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/runtime/index.js
CHANGED
|
@@ -133,8 +133,8 @@ var serviceCredentials = {
|
|
|
133
133
|
{ envVar: "REDIS_URL", required: true, description: "Redis connection URL" }
|
|
134
134
|
],
|
|
135
135
|
oauth: [
|
|
136
|
-
{ envVar: "OAUTH_CLIENT_ID", required: true, description: "OIDC client ID" },
|
|
137
|
-
{ envVar: "OAUTH_CLIENT_SECRET", required: true, description: "OIDC client secret" },
|
|
136
|
+
{ envVar: "OAUTH_CLIENT_ID", required: true, description: "OAuth/OIDC client ID" },
|
|
137
|
+
{ envVar: "OAUTH_CLIENT_SECRET", required: true, description: "OAuth/OIDC client secret" },
|
|
138
138
|
{ envVar: "OAUTH_REDIRECT_URI", required: false, description: "OIDC redirect URI (default per-call param)" },
|
|
139
139
|
{ envVar: "OIDC_ISSUER_URL", required: false, description: "OIDC issuer URL (default https://accounts.google.com)" }
|
|
140
140
|
],
|
|
@@ -3547,6 +3547,31 @@ var PROVIDER_AUTH_URLS = {
|
|
|
3547
3547
|
var PROVIDER_ISSUERS = {
|
|
3548
3548
|
google: "https://accounts.google.com"
|
|
3549
3549
|
};
|
|
3550
|
+
var PROVIDER_SERVER_METADATA = {
|
|
3551
|
+
github: {
|
|
3552
|
+
issuer: "https://github.com",
|
|
3553
|
+
authorization_endpoint: "https://github.com/login/oauth/authorize",
|
|
3554
|
+
token_endpoint: "https://github.com/login/oauth/access_token"
|
|
3555
|
+
}
|
|
3556
|
+
};
|
|
3557
|
+
var OAuthGrantNotFoundError = class extends Error {
|
|
3558
|
+
constructor(state) {
|
|
3559
|
+
super(`Invalid or expired state token: ${state}`);
|
|
3560
|
+
this.name = "OAuthGrantNotFoundError";
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
var OAuthSubjectMismatchError = class extends Error {
|
|
3564
|
+
constructor() {
|
|
3565
|
+
super("OAuth completion refused: the completing subject is not the one that started the authorization");
|
|
3566
|
+
this.name = "OAuthSubjectMismatchError";
|
|
3567
|
+
}
|
|
3568
|
+
};
|
|
3569
|
+
function assertSubject(bound, completer) {
|
|
3570
|
+
if (bound !== completer) throw new OAuthSubjectMismatchError();
|
|
3571
|
+
}
|
|
3572
|
+
function grantedScopes(scope) {
|
|
3573
|
+
return (scope ?? "").split(/[\s,]+/).filter((s) => s.length > 0);
|
|
3574
|
+
}
|
|
3550
3575
|
var PENDING_GRANT_TTL_MS = 10 * 60 * 1e3;
|
|
3551
3576
|
var InMemoryPendingGrantStore = class {
|
|
3552
3577
|
constructor() {
|
|
@@ -3571,7 +3596,7 @@ var InMemoryPendingGrantStore = class {
|
|
|
3571
3596
|
var OAuthIntegration = class extends BaseIntegration {
|
|
3572
3597
|
constructor(config) {
|
|
3573
3598
|
super(config);
|
|
3574
|
-
/** Maps state token ->
|
|
3599
|
+
/** Maps state token -> pending MOCK authorization */
|
|
3575
3600
|
this.states = /* @__PURE__ */ new Map();
|
|
3576
3601
|
/** Maps access token -> token set (mock backend) */
|
|
3577
3602
|
this.tokens = /* @__PURE__ */ new Map();
|
|
@@ -3600,12 +3625,25 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3600
3625
|
try {
|
|
3601
3626
|
let data;
|
|
3602
3627
|
switch (action) {
|
|
3603
|
-
case "authorize":
|
|
3604
|
-
|
|
3628
|
+
case "authorize": {
|
|
3629
|
+
const input = {
|
|
3630
|
+
provider: params.provider,
|
|
3631
|
+
scopes: params.scopes,
|
|
3632
|
+
redirectUri: params.redirectUri,
|
|
3633
|
+
...params.subject === void 0 ? {} : { subject: params.subject }
|
|
3634
|
+
};
|
|
3635
|
+
data = await this.executeWithRetry(() => this.startAuthorization(input));
|
|
3605
3636
|
break;
|
|
3606
|
-
|
|
3607
|
-
|
|
3637
|
+
}
|
|
3638
|
+
case "token": {
|
|
3639
|
+
const input = {
|
|
3640
|
+
code: params.code,
|
|
3641
|
+
state: params.state,
|
|
3642
|
+
...params.subject === void 0 ? {} : { subject: params.subject }
|
|
3643
|
+
};
|
|
3644
|
+
data = await this.executeWithRetry(() => this.completeAuthorization(input));
|
|
3608
3645
|
break;
|
|
3646
|
+
}
|
|
3609
3647
|
case "refresh":
|
|
3610
3648
|
data = await this.executeWithRetry(() => this.real ? this.oidcRefresh(params) : this.refresh(params));
|
|
3611
3649
|
break;
|
|
@@ -3627,6 +3665,14 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3627
3665
|
return this.handleError(action, error);
|
|
3628
3666
|
}
|
|
3629
3667
|
}
|
|
3668
|
+
/** Build the provider authorization URL and hold the pending grant (PKCE verifier, subject) under `state`. */
|
|
3669
|
+
async startAuthorization(input) {
|
|
3670
|
+
return this.real ? this.oidcAuthorize(input) : this.authorize(input);
|
|
3671
|
+
}
|
|
3672
|
+
/** Consume the pending grant once and exchange the code; throws on an unknown, replayed or expired `state`. */
|
|
3673
|
+
async completeAuthorization(input) {
|
|
3674
|
+
return this.real ? this.oidcToken(input) : this.token(input);
|
|
3675
|
+
}
|
|
3630
3676
|
// ---------------------------------------------------------------------------
|
|
3631
3677
|
// OIDC backend (openid-client)
|
|
3632
3678
|
// ---------------------------------------------------------------------------
|
|
@@ -3642,6 +3688,19 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3642
3688
|
return issuer;
|
|
3643
3689
|
}
|
|
3644
3690
|
async configurationFor(provider) {
|
|
3691
|
+
const metadata = PROVIDER_SERVER_METADATA[provider];
|
|
3692
|
+
if (metadata && !this.config.env.OIDC_ISSUER_URL) {
|
|
3693
|
+
const cached2 = this.discovered.get(metadata.issuer);
|
|
3694
|
+
if (cached2) return cached2;
|
|
3695
|
+
const configuration2 = new oidc.Configuration(
|
|
3696
|
+
metadata,
|
|
3697
|
+
this.config.env.OAUTH_CLIENT_ID,
|
|
3698
|
+
this.config.env.OAUTH_CLIENT_SECRET,
|
|
3699
|
+
oidc.ClientSecretPost(this.config.env.OAUTH_CLIENT_SECRET)
|
|
3700
|
+
);
|
|
3701
|
+
this.discovered.set(metadata.issuer, configuration2);
|
|
3702
|
+
return configuration2;
|
|
3703
|
+
}
|
|
3645
3704
|
const issuer = this.issuerFor(provider);
|
|
3646
3705
|
const cached = this.discovered.get(issuer);
|
|
3647
3706
|
if (cached) return cached;
|
|
@@ -3653,10 +3712,9 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3653
3712
|
this.discovered.set(issuer, configuration);
|
|
3654
3713
|
return configuration;
|
|
3655
3714
|
}
|
|
3656
|
-
async oidcAuthorize(
|
|
3657
|
-
const provider =
|
|
3658
|
-
const
|
|
3659
|
-
const redirectUri = params.redirectUri || this.config.env.OAUTH_REDIRECT_URI;
|
|
3715
|
+
async oidcAuthorize(input) {
|
|
3716
|
+
const { provider, scopes, subject } = input;
|
|
3717
|
+
const redirectUri = input.redirectUri || this.config.env.OAUTH_REDIRECT_URI;
|
|
3660
3718
|
const configuration = await this.configurationFor(provider);
|
|
3661
3719
|
const state = oidc.randomState();
|
|
3662
3720
|
const pkceVerifier = oidc.randomPKCECodeVerifier();
|
|
@@ -3673,17 +3731,21 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3673
3731
|
parameters.prompt = "consent";
|
|
3674
3732
|
}
|
|
3675
3733
|
const authUrl = oidc.buildAuthorizationUrl(configuration, parameters);
|
|
3676
|
-
await this.pendingStore().put(
|
|
3734
|
+
await this.pendingStore().put(
|
|
3735
|
+
state,
|
|
3736
|
+
{ provider, redirectUri, pkceVerifier, ...subject === void 0 ? {} : { subject } },
|
|
3737
|
+
PENDING_GRANT_TTL_MS
|
|
3738
|
+
);
|
|
3677
3739
|
void this.pendingStore().sweep();
|
|
3678
3740
|
return { authUrl: authUrl.toString(), state };
|
|
3679
3741
|
}
|
|
3680
|
-
async oidcToken(
|
|
3681
|
-
const code =
|
|
3682
|
-
const state = params.state;
|
|
3742
|
+
async oidcToken(input) {
|
|
3743
|
+
const { code, state } = input;
|
|
3683
3744
|
const pendingAuth = await this.pendingStore().take(state);
|
|
3684
3745
|
if (!pendingAuth) {
|
|
3685
|
-
throw new
|
|
3746
|
+
throw new OAuthGrantNotFoundError(state);
|
|
3686
3747
|
}
|
|
3748
|
+
assertSubject(pendingAuth.subject, input.subject);
|
|
3687
3749
|
const configuration = await this.configurationFor(pendingAuth.provider);
|
|
3688
3750
|
const callbackUrl = new URL(pendingAuth.redirectUri);
|
|
3689
3751
|
callbackUrl.searchParams.set("code", code);
|
|
@@ -3700,7 +3762,9 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3700
3762
|
accessToken: tokens.access_token,
|
|
3701
3763
|
refreshToken: tokens.refresh_token ?? "",
|
|
3702
3764
|
expiresIn: tokens.expires_in ?? 3600,
|
|
3703
|
-
tokenType: "bearer"
|
|
3765
|
+
tokenType: "bearer",
|
|
3766
|
+
scope: grantedScopes(tokens.scope),
|
|
3767
|
+
...pendingAuth.subject === void 0 ? {} : { subject: pendingAuth.subject }
|
|
3704
3768
|
};
|
|
3705
3769
|
}
|
|
3706
3770
|
async oidcRefresh(params) {
|
|
@@ -3763,13 +3827,11 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3763
3827
|
// ---------------------------------------------------------------------------
|
|
3764
3828
|
// Mock backend actions
|
|
3765
3829
|
// ---------------------------------------------------------------------------
|
|
3766
|
-
async authorize(
|
|
3767
|
-
const provider =
|
|
3768
|
-
const scopes = params.scopes;
|
|
3769
|
-
const redirectUri = params.redirectUri;
|
|
3830
|
+
async authorize(input) {
|
|
3831
|
+
const { provider, scopes, redirectUri, subject } = input;
|
|
3770
3832
|
this.logger.debug("OAuth AUTHORIZE", { provider, scopes: scopes.join(", "), redirectUri });
|
|
3771
3833
|
const state = this.generateToken(16);
|
|
3772
|
-
this.states.set(state, provider);
|
|
3834
|
+
this.states.set(state, { provider, scopes, ...subject === void 0 ? {} : { subject } });
|
|
3773
3835
|
const baseUrl = PROVIDER_AUTH_URLS[provider];
|
|
3774
3836
|
const queryParams = new URLSearchParams({
|
|
3775
3837
|
response_type: "code",
|
|
@@ -3781,17 +3843,15 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3781
3843
|
const authUrl = `${baseUrl}?${queryParams.toString()}`;
|
|
3782
3844
|
return { authUrl, state };
|
|
3783
3845
|
}
|
|
3784
|
-
async token(
|
|
3785
|
-
const code =
|
|
3786
|
-
const state = params.state;
|
|
3846
|
+
async token(input) {
|
|
3847
|
+
const { code, state } = input;
|
|
3787
3848
|
this.logger.debug("OAuth TOKEN", { code, state });
|
|
3788
|
-
const
|
|
3789
|
-
if (!
|
|
3790
|
-
throw new
|
|
3791
|
-
`Invalid or expired state token: ${state}`
|
|
3792
|
-
);
|
|
3849
|
+
const pending = this.states.get(state);
|
|
3850
|
+
if (!pending) {
|
|
3851
|
+
throw new OAuthGrantNotFoundError(state);
|
|
3793
3852
|
}
|
|
3794
3853
|
this.states.delete(state);
|
|
3854
|
+
assertSubject(pending.subject, input.subject);
|
|
3795
3855
|
const accessToken = this.generateToken(32);
|
|
3796
3856
|
const refreshToken = this.generateToken(32);
|
|
3797
3857
|
const expiresIn = 3600;
|
|
@@ -3803,8 +3863,15 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3803
3863
|
this.tokens.set(accessToken, tokenSet);
|
|
3804
3864
|
this.refreshIndex.set(refreshToken, accessToken);
|
|
3805
3865
|
const sub = this.generateToken(8);
|
|
3806
|
-
this.sessions.set(accessToken, this.generateMockUser(provider, sub));
|
|
3807
|
-
return {
|
|
3866
|
+
this.sessions.set(accessToken, this.generateMockUser(pending.provider, sub));
|
|
3867
|
+
return {
|
|
3868
|
+
accessToken,
|
|
3869
|
+
refreshToken,
|
|
3870
|
+
expiresIn,
|
|
3871
|
+
tokenType: "bearer",
|
|
3872
|
+
scope: pending.scopes,
|
|
3873
|
+
...pending.subject === void 0 ? {} : { subject: pending.subject }
|
|
3874
|
+
};
|
|
3808
3875
|
}
|
|
3809
3876
|
async refresh(params) {
|
|
3810
3877
|
const refreshToken = params.refreshToken;
|