@backstage/plugin-auth-backend-module-openshift-provider 0.0.0-nightly-20250910023713
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 +15 -0
- package/README.md +5 -0
- package/config.d.ts +44 -0
- package/dist/authenticator.cjs.js +121 -0
- package/dist/authenticator.cjs.js.map +1 -0
- package/dist/index.cjs.js +12 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/module.cjs.js +30 -0
- package/dist/module.cjs.js.map +1 -0
- package/dist/resolvers.cjs.js +36 -0
- package/dist/resolvers.cjs.js.map +1 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @backstage/plugin-auth-backend-module-openshift-provider
|
|
2
|
+
|
|
3
|
+
## 0.0.0-nightly-20250910023713
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5a84253: Add new `auth-backend-module-openshift-provider`. This authentication provider enables Backstage to sign in with OpenShift.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/plugin-auth-node@0.0.0-nightly-20250910023713
|
|
13
|
+
- @backstage/backend-plugin-api@0.0.0-nightly-20250910023713
|
|
14
|
+
- @backstage/catalog-model@1.7.5
|
|
15
|
+
- @backstage/types@1.2.1
|
package/README.md
ADDED
package/config.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { HumanDuration } from '@backstage/types';
|
|
17
|
+
|
|
18
|
+
export interface Config {
|
|
19
|
+
auth?: {
|
|
20
|
+
providers?: {
|
|
21
|
+
/** @visibility frontend */
|
|
22
|
+
openshift?: {
|
|
23
|
+
[authEnv: string]: {
|
|
24
|
+
clientId: string;
|
|
25
|
+
/**
|
|
26
|
+
* @visibility secret
|
|
27
|
+
*/
|
|
28
|
+
clientSecret: string;
|
|
29
|
+
authorizationUrl: string;
|
|
30
|
+
tokenUrl: string;
|
|
31
|
+
callbackUrl?: string;
|
|
32
|
+
openshiftApiServerUrl: string;
|
|
33
|
+
signIn?: {
|
|
34
|
+
resolvers: Array<{
|
|
35
|
+
resolver: 'displayNameMatchingUserEntityName';
|
|
36
|
+
dangerouslyAllowSignInWithoutUserInCatalog?: boolean;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
sessionDuration?: HumanDuration | string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
4
|
+
var node_crypto = require('node:crypto');
|
|
5
|
+
var OAuth2Strategy = require('passport-oauth2');
|
|
6
|
+
var zod = require('zod');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var OAuth2Strategy__default = /*#__PURE__*/_interopDefaultCompat(OAuth2Strategy);
|
|
11
|
+
|
|
12
|
+
const OpenShiftUser = zod.z.object({
|
|
13
|
+
metadata: zod.z.object({
|
|
14
|
+
name: zod.z.string()
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
const openshiftAuthenticator = pluginAuthNode.createOAuthAuthenticator({
|
|
18
|
+
defaultProfileTransform: pluginAuthNode.PassportOAuthAuthenticatorHelper.defaultProfileTransform,
|
|
19
|
+
scopes: {
|
|
20
|
+
required: ["user:full"]
|
|
21
|
+
},
|
|
22
|
+
initialize({ callbackUrl, config }) {
|
|
23
|
+
const clientId = config.getString("clientId");
|
|
24
|
+
const clientSecret = config.getString("clientSecret");
|
|
25
|
+
const authorizationUrl = config.getString("authorizationUrl");
|
|
26
|
+
const tokenUrl = config.getString("tokenUrl");
|
|
27
|
+
const openshiftApiServerUrl = config.getString("openshiftApiServerUrl");
|
|
28
|
+
const strategy = new OAuth2Strategy__default.default(
|
|
29
|
+
{
|
|
30
|
+
clientID: clientId,
|
|
31
|
+
clientSecret,
|
|
32
|
+
callbackURL: callbackUrl,
|
|
33
|
+
authorizationURL: authorizationUrl,
|
|
34
|
+
tokenURL: tokenUrl,
|
|
35
|
+
passReqToCallback: false
|
|
36
|
+
},
|
|
37
|
+
(accessToken, refreshToken, params, fullProfile, done) => {
|
|
38
|
+
done(void 0, { fullProfile, params, accessToken }, { refreshToken });
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
strategy.userProfile = function userProfile(accessToken, done) {
|
|
42
|
+
this._oauth2.useAuthorizationHeaderforGET(true);
|
|
43
|
+
this._oauth2.get(
|
|
44
|
+
`${openshiftApiServerUrl}/apis/user.openshift.io/v1/users/~`,
|
|
45
|
+
accessToken,
|
|
46
|
+
(error, data, _) => {
|
|
47
|
+
if (error !== null && error.statusCode !== 200) {
|
|
48
|
+
done(new Error(`HTTP error! Status: ${error.statusCode}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!data) {
|
|
52
|
+
done(new Error("No data provided!"));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (typeof data !== "string") {
|
|
56
|
+
done(new Error("Data of type Buffer is not supported!"));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const user = OpenShiftUser.parse(JSON.parse(data));
|
|
60
|
+
done(null, { displayName: user.metadata.name });
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
openshiftApiServerUrl,
|
|
66
|
+
helper: pluginAuthNode.PassportOAuthAuthenticatorHelper.from(strategy)
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
async start(input, { helper }) {
|
|
70
|
+
return helper.start(input, {
|
|
71
|
+
accessType: "offline",
|
|
72
|
+
prompt: "consent"
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
async authenticate(input, { helper }) {
|
|
76
|
+
const { fullProfile, session } = await helper.authenticate(input);
|
|
77
|
+
session.refreshToken = session.accessToken;
|
|
78
|
+
session.refreshTokenExpiresInSeconds = session.expiresInSeconds;
|
|
79
|
+
return { fullProfile, session };
|
|
80
|
+
},
|
|
81
|
+
async refresh(input, { helper }) {
|
|
82
|
+
const accessToken = input.refreshToken;
|
|
83
|
+
const fullProfile = await helper.fetchProfile(accessToken).catch((error) => {
|
|
84
|
+
if (error.oauthError?.statusCode === 401) {
|
|
85
|
+
throw new Error("Invalid access token");
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
fullProfile,
|
|
91
|
+
session: {
|
|
92
|
+
accessToken,
|
|
93
|
+
tokenType: "bearer",
|
|
94
|
+
scope: input.scope,
|
|
95
|
+
refreshToken: input.refreshToken
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
async logout(input, { openshiftApiServerUrl, helper }) {
|
|
100
|
+
const accessToken = input.refreshToken;
|
|
101
|
+
if (!accessToken) {
|
|
102
|
+
throw new Error("access token/refresh token needs to be set for logout");
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await helper.fetchProfile(accessToken);
|
|
106
|
+
} catch {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const tokenName = node_crypto.createHash("sha256").update(accessToken.slice("sha256~".length)).digest().toString("base64url");
|
|
110
|
+
const response = await fetch(
|
|
111
|
+
`${openshiftApiServerUrl}/apis/oauth.openshift.io/v1/oauthaccesstokens/sha256~${tokenName}`,
|
|
112
|
+
{ method: "DELETE", headers: { Authorization: `Bearer ${accessToken}` } }
|
|
113
|
+
);
|
|
114
|
+
if (response.status === 401) {
|
|
115
|
+
throw new Error("unauthorized");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
exports.openshiftAuthenticator = openshiftAuthenticator;
|
|
121
|
+
//# sourceMappingURL=authenticator.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticator.cjs.js","sources":["../src/authenticator.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createOAuthAuthenticator,\n PassportOAuthAuthenticatorHelper,\n PassportOAuthDoneCallback,\n PassportProfile,\n} from '@backstage/plugin-auth-node';\nimport { createHash } from 'node:crypto';\nimport OAuth2Strategy from 'passport-oauth2';\nimport { z } from 'zod';\n\n/** @public */\nexport interface OpenShiftAuthenticatorContext {\n openshiftApiServerUrl: string;\n helper: PassportOAuthAuthenticatorHelper;\n}\n\n/** @private\n * Schema for user.openshift.io/v1,\n * see https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/user_and_group_apis/user-user-openshift-io-v1#user-user-openshift-io-v1\n */\nconst OpenShiftUser = z.object({\n metadata: z.object({\n name: z.string(),\n }),\n});\n\n/** @public */\nexport const openshiftAuthenticator = createOAuthAuthenticator<\n OpenShiftAuthenticatorContext,\n PassportProfile\n>({\n defaultProfileTransform:\n PassportOAuthAuthenticatorHelper.defaultProfileTransform,\n scopes: {\n required: ['user:full'],\n },\n initialize({ callbackUrl, config }) {\n const clientId = config.getString('clientId');\n const clientSecret = config.getString('clientSecret');\n const authorizationUrl = config.getString('authorizationUrl');\n const tokenUrl = config.getString('tokenUrl');\n const openshiftApiServerUrl = config.getString('openshiftApiServerUrl');\n\n // userUrl: `${openshiftApiServerUrl}/apis/user.openshift.io/v1/users/~`,\n const strategy = new OAuth2Strategy(\n {\n clientID: clientId,\n clientSecret: clientSecret,\n callbackURL: callbackUrl,\n authorizationURL: authorizationUrl,\n tokenURL: tokenUrl,\n passReqToCallback: false,\n },\n (\n accessToken: any,\n refreshToken: string,\n params: any,\n fullProfile: PassportProfile,\n done: PassportOAuthDoneCallback,\n ) => {\n done(undefined, { fullProfile, params, accessToken }, { refreshToken });\n },\n );\n\n strategy.userProfile = function userProfile(\n accessToken: string,\n done: (err?: unknown, profile?: any) => void,\n ): void {\n this._oauth2.useAuthorizationHeaderforGET(true);\n\n this._oauth2.get(\n `${openshiftApiServerUrl}/apis/user.openshift.io/v1/users/~`,\n accessToken,\n (error, data, _) => {\n if (error !== null && error.statusCode !== 200) {\n done(new Error(`HTTP error! Status: ${error.statusCode}`));\n return;\n }\n\n if (!data) {\n done(new Error('No data provided!'));\n return;\n }\n\n if (typeof data !== 'string') {\n done(new Error('Data of type Buffer is not supported!'));\n return;\n }\n\n const user = OpenShiftUser.parse(JSON.parse(data));\n done(null, { displayName: user.metadata.name });\n },\n );\n };\n\n return {\n openshiftApiServerUrl,\n helper: PassportOAuthAuthenticatorHelper.from(strategy),\n };\n },\n async start(input, { helper }) {\n return helper.start(input, {\n accessType: 'offline',\n prompt: 'consent',\n });\n },\n async authenticate(input, { helper }) {\n // Same workaround as the GitHub provider; see https://github.com/backstage/backstage/issues/25383\n const { fullProfile, session } = await helper.authenticate(input);\n session.refreshToken = session.accessToken;\n session.refreshTokenExpiresInSeconds = session.expiresInSeconds;\n return { fullProfile, session };\n },\n async refresh(input, { helper }) {\n // Because the session is refreshed on login, this override is crucial,\n // see https://github.com/backstage/backstage/issues/25383\n const accessToken = input.refreshToken;\n\n const fullProfile = await helper.fetchProfile(accessToken).catch(error => {\n if (error.oauthError?.statusCode === 401) {\n throw new Error('Invalid access token');\n }\n throw error;\n });\n\n return {\n fullProfile,\n session: {\n accessToken,\n tokenType: 'bearer',\n scope: input.scope,\n refreshToken: input.refreshToken,\n },\n };\n },\n async logout(input, { openshiftApiServerUrl, helper }) {\n // Due to the implementation of createOAuthRouteHandlers, only the refresh token is set.\n // In this provider, the refresh token actually IS the access token.\n const accessToken = input.refreshToken;\n if (!accessToken) {\n throw new Error('access token/refresh token needs to be set for logout');\n }\n\n // Check if access token is still valid.\n try {\n await helper.fetchProfile(accessToken);\n } catch {\n // Invalid token, no need to delete OAuthAccessToken.\n return;\n }\n\n // Calculate token name, see:\n // https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/oauth_apis/oauthaccesstoken-oauth-openshift-io-v1#apis-oauth-openshift-io-v1-oauthaccesstokens\n const tokenName = createHash('sha256')\n .update(accessToken.slice('sha256~'.length))\n .digest()\n .toString('base64url');\n\n const response = await fetch(\n `${openshiftApiServerUrl}/apis/oauth.openshift.io/v1/oauthaccesstokens/sha256~${tokenName}`,\n { method: 'DELETE', headers: { Authorization: `Bearer ${accessToken}` } },\n );\n\n if (response.status === 401) {\n throw new Error('unauthorized');\n }\n },\n});\n"],"names":["z","createOAuthAuthenticator","PassportOAuthAuthenticatorHelper","OAuth2Strategy","createHash"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var authenticator = require('./authenticator.cjs.js');
|
|
6
|
+
var module$1 = require('./module.cjs.js');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
exports.openshiftAuthenticator = authenticator.openshiftAuthenticator;
|
|
11
|
+
exports.default = module$1.authModuleOpenshiftProvider;
|
|
12
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as _backstage_plugin_auth_node from '@backstage/plugin-auth-node';
|
|
2
|
+
import { PassportOAuthAuthenticatorHelper, PassportProfile } from '@backstage/plugin-auth-node';
|
|
3
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
4
|
+
|
|
5
|
+
/** @public */
|
|
6
|
+
interface OpenShiftAuthenticatorContext {
|
|
7
|
+
openshiftApiServerUrl: string;
|
|
8
|
+
helper: PassportOAuthAuthenticatorHelper;
|
|
9
|
+
}
|
|
10
|
+
/** @public */
|
|
11
|
+
declare const openshiftAuthenticator: _backstage_plugin_auth_node.OAuthAuthenticator<OpenShiftAuthenticatorContext, PassportProfile>;
|
|
12
|
+
|
|
13
|
+
/** @public */
|
|
14
|
+
declare const authModuleOpenshiftProvider: _backstage_backend_plugin_api.BackendFeature;
|
|
15
|
+
|
|
16
|
+
export { type OpenShiftAuthenticatorContext, authModuleOpenshiftProvider as default, openshiftAuthenticator };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
5
|
+
var authenticator = require('./authenticator.cjs.js');
|
|
6
|
+
var resolvers = require('./resolvers.cjs.js');
|
|
7
|
+
|
|
8
|
+
const authModuleOpenshiftProvider = backendPluginApi.createBackendModule({
|
|
9
|
+
pluginId: "auth",
|
|
10
|
+
moduleId: "openshift-provider",
|
|
11
|
+
register(reg) {
|
|
12
|
+
reg.registerInit({
|
|
13
|
+
deps: { providers: pluginAuthNode.authProvidersExtensionPoint },
|
|
14
|
+
async init({ providers }) {
|
|
15
|
+
providers.registerProvider({
|
|
16
|
+
providerId: "openshift",
|
|
17
|
+
factory: pluginAuthNode.createOAuthProviderFactory({
|
|
18
|
+
authenticator: authenticator.openshiftAuthenticator,
|
|
19
|
+
signInResolverFactories: {
|
|
20
|
+
...resolvers.openshiftSignInResolvers
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
exports.authModuleOpenshiftProvider = authModuleOpenshiftProvider;
|
|
30
|
+
//# sourceMappingURL=module.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.cjs.js","sources":["../src/module.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createBackendModule } from '@backstage/backend-plugin-api';\nimport {\n authProvidersExtensionPoint,\n createOAuthProviderFactory,\n} from '@backstage/plugin-auth-node';\nimport { openshiftAuthenticator } from './authenticator';\nimport { openshiftSignInResolvers } from './resolvers';\n\n/** @public */\nexport const authModuleOpenshiftProvider = createBackendModule({\n pluginId: 'auth',\n moduleId: 'openshift-provider',\n register(reg) {\n reg.registerInit({\n deps: { providers: authProvidersExtensionPoint },\n async init({ providers }) {\n providers.registerProvider({\n providerId: 'openshift',\n factory: createOAuthProviderFactory({\n authenticator: openshiftAuthenticator,\n signInResolverFactories: {\n ...openshiftSignInResolvers,\n },\n }),\n });\n },\n });\n },\n});\n"],"names":["createBackendModule","authProvidersExtensionPoint","createOAuthProviderFactory","openshiftAuthenticator","openshiftSignInResolvers"],"mappings":";;;;;;;AAyBO,MAAM,8BAA8BA,oCAAA,CAAoB;AAAA,EAC7D,QAAA,EAAU,MAAA;AAAA,EACV,QAAA,EAAU,oBAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM,EAAE,SAAA,EAAWC,0CAAA,EAA4B;AAAA,MAC/C,MAAM,IAAA,CAAK,EAAE,SAAA,EAAU,EAAG;AACxB,QAAA,SAAA,CAAU,gBAAA,CAAiB;AAAA,UACzB,UAAA,EAAY,WAAA;AAAA,UACZ,SAASC,yCAAA,CAA2B;AAAA,YAClC,aAAA,EAAeC,oCAAA;AAAA,YACf,uBAAA,EAAyB;AAAA,cACvB,GAAGC;AAAA;AACL,WACD;AAAA,SACF,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
4
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
|
|
7
|
+
exports.openshiftSignInResolvers = void 0;
|
|
8
|
+
((openshiftSignInResolvers2) => {
|
|
9
|
+
openshiftSignInResolvers2.displayNameMatchingUserEntityName = pluginAuthNode.createSignInResolverFactory({
|
|
10
|
+
optionsSchema: zod.z.object({
|
|
11
|
+
dangerouslyAllowSignInWithoutUserInCatalog: zod.z.boolean().optional()
|
|
12
|
+
}).optional(),
|
|
13
|
+
create(options = {}) {
|
|
14
|
+
return async (info, ctx) => {
|
|
15
|
+
const { displayName } = info.profile;
|
|
16
|
+
if (!displayName) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`OpenShift user profile does not contain a displayName`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const userRef = catalogModel.stringifyEntityRef({
|
|
22
|
+
kind: "User",
|
|
23
|
+
name: displayName,
|
|
24
|
+
namespace: catalogModel.DEFAULT_NAMESPACE
|
|
25
|
+
});
|
|
26
|
+
return await ctx.signInWithCatalogUser(
|
|
27
|
+
{ entityRef: userRef },
|
|
28
|
+
{
|
|
29
|
+
dangerousEntityRefFallback: options?.dangerouslyAllowSignInWithoutUserInCatalog ? { entityRef: { name: displayName } } : void 0
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
})(exports.openshiftSignInResolvers || (exports.openshiftSignInResolvers = {}));
|
|
36
|
+
//# sourceMappingURL=resolvers.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolvers.cjs.js","sources":["../src/resolvers.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createSignInResolverFactory,\n OAuthAuthenticatorResult,\n PassportProfile,\n SignInInfo,\n} from '@backstage/plugin-auth-node';\n\nimport {\n DEFAULT_NAMESPACE,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { z } from 'zod';\n\nexport namespace openshiftSignInResolvers {\n export const displayNameMatchingUserEntityName = createSignInResolverFactory({\n optionsSchema: z\n .object({\n dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(),\n })\n .optional(),\n create(options = {}) {\n return async (\n info: SignInInfo<OAuthAuthenticatorResult<PassportProfile>>,\n ctx,\n ) => {\n const { displayName } = info.profile;\n\n if (!displayName) {\n throw new Error(\n `OpenShift user profile does not contain a displayName`,\n );\n }\n\n const userRef = stringifyEntityRef({\n kind: 'User',\n name: displayName,\n namespace: DEFAULT_NAMESPACE,\n });\n\n return await ctx.signInWithCatalogUser(\n { entityRef: userRef },\n {\n dangerousEntityRefFallback:\n options?.dangerouslyAllowSignInWithoutUserInCatalog\n ? { entityRef: { name: displayName } }\n : undefined,\n },\n );\n };\n },\n });\n}\n"],"names":["openshiftSignInResolvers","createSignInResolverFactory","z","stringifyEntityRef","DEFAULT_NAMESPACE"],"mappings":";;;;;;AA6BiBA;AAAA,CAAV,CAAUA,yBAAAA,KAAV;AACE,EAAMA,yBAAAA,CAAA,oCAAoCC,0CAAA,CAA4B;AAAA,IAC3E,aAAA,EAAeC,MACZ,MAAA,CAAO;AAAA,MACN,0CAAA,EAA4CA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAA;AAAS,KAClE,EACA,QAAA,EAAS;AAAA,IACZ,MAAA,CAAO,OAAA,GAAU,EAAC,EAAG;AACnB,MAAA,OAAO,OACL,MACA,GAAA,KACG;AACH,QAAA,MAAM,EAAE,WAAA,EAAY,GAAI,IAAA,CAAK,OAAA;AAE7B,QAAA,IAAI,CAAC,WAAA,EAAa;AAChB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,qDAAA;AAAA,WACF;AAAA,QACF;AAEA,QAAA,MAAM,UAAUC,+BAAA,CAAmB;AAAA,UACjC,IAAA,EAAM,MAAA;AAAA,UACN,IAAA,EAAM,WAAA;AAAA,UACN,SAAA,EAAWC;AAAA,SACZ,CAAA;AAED,QAAA,OAAO,MAAM,GAAA,CAAI,qBAAA;AAAA,UACf,EAAE,WAAW,OAAA,EAAQ;AAAA,UACrB;AAAA,YACE,0BAAA,EACE,SAAS,0CAAA,GACL,EAAE,WAAW,EAAE,IAAA,EAAM,WAAA,EAAY,EAAE,GACnC;AAAA;AACR,SACF;AAAA,MACF,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AAAA,CAAA,EArCcJ,gCAAA,KAAAA,gCAAA,GAAA,EAAA,CAAA,CAAA;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@backstage/plugin-auth-backend-module-openshift-provider",
|
|
3
|
+
"version": "0.0.0-nightly-20250910023713",
|
|
4
|
+
"description": "The OpenShift backend module for the auth plugin.",
|
|
5
|
+
"backstage": {
|
|
6
|
+
"role": "backend-plugin-module",
|
|
7
|
+
"pluginId": "auth",
|
|
8
|
+
"pluginPackage": "@backstage/plugin-auth-backend",
|
|
9
|
+
"features": {
|
|
10
|
+
".": "@backstage/BackendFeature"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"main": "dist/index.cjs.js",
|
|
16
|
+
"types": "dist/index.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/backstage/backstage",
|
|
21
|
+
"directory": "plugins/auth-backend-module-openshift-provider"
|
|
22
|
+
},
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"main": "dist/index.cjs.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"config.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "backstage-cli package build",
|
|
32
|
+
"clean": "backstage-cli package clean",
|
|
33
|
+
"lint": "backstage-cli package lint",
|
|
34
|
+
"prepack": "backstage-cli package prepack",
|
|
35
|
+
"postpack": "backstage-cli package postpack",
|
|
36
|
+
"start": "backstage-cli package start",
|
|
37
|
+
"test": "backstage-cli package test"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@backstage/backend-plugin-api": "0.0.0-nightly-20250910023713",
|
|
41
|
+
"@backstage/catalog-model": "1.7.5",
|
|
42
|
+
"@backstage/plugin-auth-node": "0.0.0-nightly-20250910023713",
|
|
43
|
+
"@backstage/types": "1.2.1",
|
|
44
|
+
"passport-oauth2": "^1.8.0",
|
|
45
|
+
"zod": "^3.24.2"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@backstage/backend-defaults": "0.0.0-nightly-20250910023713",
|
|
49
|
+
"@backstage/backend-test-utils": "0.0.0-nightly-20250910023713",
|
|
50
|
+
"@backstage/cli": "0.0.0-nightly-20250910023713",
|
|
51
|
+
"@backstage/config": "1.3.3",
|
|
52
|
+
"@backstage/plugin-auth-backend": "0.0.0-nightly-20250910023713",
|
|
53
|
+
"express": "^4.18.2",
|
|
54
|
+
"msw": "^2.7.3",
|
|
55
|
+
"supertest": "^7.1.0"
|
|
56
|
+
},
|
|
57
|
+
"configSchema": "config.d.ts",
|
|
58
|
+
"typesVersions": {
|
|
59
|
+
"*": {
|
|
60
|
+
"package.json": [
|
|
61
|
+
"package.json"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|