@backstage/plugin-auth-backend 0.18.2-next.2 → 0.18.2-next.3
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 +28 -0
- package/dist/index.cjs.js +147 -26
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +20 -2
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.18.2-next.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 475abd1dc3f: The `microsoft` (i.e. Azure) auth provider now supports negotiating tokens for
|
|
8
|
+
Azure resources besides Microsoft Graph (e.g. AKS, Virtual Machines, Machine
|
|
9
|
+
Learning Services, etc.). When the `/frame/handler` endpoint is called with an
|
|
10
|
+
authorization code for a non-Microsoft Graph scope, the user profile will not be
|
|
11
|
+
fetched. Similarly no user profile or photo data will be fetched by the backend
|
|
12
|
+
if the `/refresh` endpoint is called with the `scope` query parameter strictly
|
|
13
|
+
containing scopes for resources besides Microsoft Graph.
|
|
14
|
+
|
|
15
|
+
Furthermore, the `offline_access` scope will be requested by default, even when
|
|
16
|
+
it is not mentioned in the argument to `getAccessToken`. This means that any
|
|
17
|
+
Azure access token can be automatically refreshed, even if the user has not
|
|
18
|
+
signed in via Azure.
|
|
19
|
+
|
|
20
|
+
- 6a900951336: Add common identify resolvers for `oidc` auth provider.
|
|
21
|
+
- a0ef1ec7349: Export Azure Easy Auth provider so it can actually be used.
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- @backstage/catalog-model@1.3.0-next.0
|
|
24
|
+
- @backstage/backend-common@0.18.4-next.2
|
|
25
|
+
- @backstage/catalog-client@1.4.1-next.1
|
|
26
|
+
- @backstage/config@1.0.7
|
|
27
|
+
- @backstage/errors@1.1.5
|
|
28
|
+
- @backstage/types@1.0.2
|
|
29
|
+
- @backstage/plugin-auth-node@0.2.13-next.2
|
|
30
|
+
|
|
3
31
|
## 0.18.2-next.2
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -1872,6 +1872,11 @@ const google = createAuthProviderIntegration({
|
|
|
1872
1872
|
const BACKSTAGE_SESSION_EXPIRATION = 3600;
|
|
1873
1873
|
class MicrosoftAuthProvider {
|
|
1874
1874
|
constructor(options) {
|
|
1875
|
+
this.skipUserProfile = (accessToken) => {
|
|
1876
|
+
const { aud, scp } = jose.decodeJwt(accessToken);
|
|
1877
|
+
const hasGraphReadScope = aud === "00000003-0000-0000-c000-000000000000" && scp.split(" ").map((s) => s.toLowerCase()).includes("user.read");
|
|
1878
|
+
return !hasGraphReadScope;
|
|
1879
|
+
};
|
|
1875
1880
|
this.signInResolver = options.signInResolver;
|
|
1876
1881
|
this.authHandler = options.authHandler;
|
|
1877
1882
|
this.logger = options.logger;
|
|
@@ -1883,7 +1888,10 @@ class MicrosoftAuthProvider {
|
|
|
1883
1888
|
callbackURL: options.callbackUrl,
|
|
1884
1889
|
authorizationURL: options.authorizationUrl,
|
|
1885
1890
|
tokenURL: options.tokenUrl,
|
|
1886
|
-
passReqToCallback: false
|
|
1891
|
+
passReqToCallback: false,
|
|
1892
|
+
skipUserProfile: (accessToken, done) => {
|
|
1893
|
+
done(null, this.skipUserProfile(accessToken));
|
|
1894
|
+
}
|
|
1887
1895
|
},
|
|
1888
1896
|
(accessToken, refreshToken, params, fullProfile, done) => {
|
|
1889
1897
|
done(void 0, { fullProfile, accessToken, params }, { refreshToken });
|
|
@@ -1909,43 +1917,46 @@ class MicrosoftAuthProvider {
|
|
|
1909
1917
|
req.refreshToken,
|
|
1910
1918
|
req.scope
|
|
1911
1919
|
);
|
|
1912
|
-
const fullProfile = await executeFetchUserProfileStrategy(
|
|
1913
|
-
this._strategy,
|
|
1914
|
-
accessToken
|
|
1915
|
-
);
|
|
1916
1920
|
return {
|
|
1917
1921
|
response: await this.handleResult({
|
|
1918
|
-
fullProfile,
|
|
1919
1922
|
params,
|
|
1920
|
-
accessToken
|
|
1923
|
+
accessToken,
|
|
1924
|
+
...!this.skipUserProfile(accessToken) && {
|
|
1925
|
+
fullProfile: await executeFetchUserProfileStrategy(
|
|
1926
|
+
this._strategy,
|
|
1927
|
+
accessToken
|
|
1928
|
+
)
|
|
1929
|
+
}
|
|
1921
1930
|
}),
|
|
1922
1931
|
refreshToken
|
|
1923
1932
|
};
|
|
1924
1933
|
}
|
|
1925
1934
|
async handleResult(result) {
|
|
1926
|
-
|
|
1927
|
-
result.fullProfile
|
|
1928
|
-
|
|
1935
|
+
let profile = {};
|
|
1936
|
+
if (result.fullProfile) {
|
|
1937
|
+
const photo = await this.getUserPhoto(result.accessToken);
|
|
1938
|
+
result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
|
|
1939
|
+
({ profile } = await this.authHandler(
|
|
1940
|
+
result,
|
|
1941
|
+
this.resolverContext
|
|
1942
|
+
));
|
|
1943
|
+
}
|
|
1929
1944
|
const expiresInSeconds = result.params.expires_in === void 0 ? BACKSTAGE_SESSION_EXPIRATION : Math.min(result.params.expires_in, BACKSTAGE_SESSION_EXPIRATION);
|
|
1930
|
-
|
|
1945
|
+
return {
|
|
1931
1946
|
providerInfo: {
|
|
1932
|
-
idToken: result.params.id_token,
|
|
1933
1947
|
accessToken: result.accessToken,
|
|
1934
1948
|
scope: result.params.scope,
|
|
1935
|
-
expiresInSeconds
|
|
1949
|
+
expiresInSeconds,
|
|
1950
|
+
...{ idToken: result.params.id_token }
|
|
1936
1951
|
},
|
|
1937
|
-
profile
|
|
1952
|
+
profile,
|
|
1953
|
+
...result.fullProfile && this.signInResolver && {
|
|
1954
|
+
backstageIdentity: await this.signInResolver(
|
|
1955
|
+
{ result, profile },
|
|
1956
|
+
this.resolverContext
|
|
1957
|
+
)
|
|
1958
|
+
}
|
|
1938
1959
|
};
|
|
1939
|
-
if (this.signInResolver) {
|
|
1940
|
-
response.backstageIdentity = await this.signInResolver(
|
|
1941
|
-
{
|
|
1942
|
-
result,
|
|
1943
|
-
profile
|
|
1944
|
-
},
|
|
1945
|
-
this.resolverContext
|
|
1946
|
-
);
|
|
1947
|
-
}
|
|
1948
|
-
return response;
|
|
1949
1960
|
}
|
|
1950
1961
|
async getUserPhoto(accessToken) {
|
|
1951
1962
|
try {
|
|
@@ -1979,7 +1990,7 @@ const microsoft = createAuthProviderIntegration({
|
|
|
1979
1990
|
const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
|
|
1980
1991
|
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
1981
1992
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1982
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1993
|
+
profile: makeProfileInfo(fullProfile != null ? fullProfile : {}, params.id_token)
|
|
1983
1994
|
});
|
|
1984
1995
|
const provider = new MicrosoftAuthProvider({
|
|
1985
1996
|
clientId,
|
|
@@ -2382,6 +2393,16 @@ const oidc = createAuthProviderIntegration({
|
|
|
2382
2393
|
callbackUrl
|
|
2383
2394
|
});
|
|
2384
2395
|
});
|
|
2396
|
+
},
|
|
2397
|
+
resolvers: {
|
|
2398
|
+
/**
|
|
2399
|
+
* Looks up the user by matching their email local part to the entity name.
|
|
2400
|
+
*/
|
|
2401
|
+
emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
|
|
2402
|
+
/**
|
|
2403
|
+
* Looks up the user by matching their email to the entity email.
|
|
2404
|
+
*/
|
|
2405
|
+
emailMatchingUserEntityProfileEmail: () => commonByEmailResolver
|
|
2385
2406
|
}
|
|
2386
2407
|
});
|
|
2387
2408
|
|
|
@@ -2934,6 +2955,104 @@ const bitbucketServer = createAuthProviderIntegration({
|
|
|
2934
2955
|
}
|
|
2935
2956
|
});
|
|
2936
2957
|
|
|
2958
|
+
const ID_TOKEN_HEADER = "x-ms-token-aad-id-token";
|
|
2959
|
+
const ACCESS_TOKEN_HEADER = "x-ms-token-aad-access-token";
|
|
2960
|
+
class EasyAuthAuthProvider {
|
|
2961
|
+
constructor(options) {
|
|
2962
|
+
this.authHandler = options.authHandler;
|
|
2963
|
+
this.signInResolver = options.signInResolver;
|
|
2964
|
+
this.resolverContext = options.resolverContext;
|
|
2965
|
+
}
|
|
2966
|
+
frameHandler() {
|
|
2967
|
+
return Promise.resolve(void 0);
|
|
2968
|
+
}
|
|
2969
|
+
async refresh(req, res) {
|
|
2970
|
+
const result = await this.getResult(req);
|
|
2971
|
+
const response = await this.handleResult(result);
|
|
2972
|
+
res.json(response);
|
|
2973
|
+
}
|
|
2974
|
+
start() {
|
|
2975
|
+
return Promise.resolve(void 0);
|
|
2976
|
+
}
|
|
2977
|
+
async getResult(req) {
|
|
2978
|
+
const idToken = req.header(ID_TOKEN_HEADER);
|
|
2979
|
+
const accessToken = req.header(ACCESS_TOKEN_HEADER);
|
|
2980
|
+
if (idToken === void 0) {
|
|
2981
|
+
throw new errors.AuthenticationError(`Missing ${ID_TOKEN_HEADER} header`);
|
|
2982
|
+
}
|
|
2983
|
+
return {
|
|
2984
|
+
fullProfile: this.idTokenToProfile(idToken),
|
|
2985
|
+
accessToken
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
idTokenToProfile(idToken) {
|
|
2989
|
+
const claims = jose.decodeJwt(idToken);
|
|
2990
|
+
if (claims.ver !== "2.0") {
|
|
2991
|
+
throw new Error("id_token is not version 2.0 ");
|
|
2992
|
+
}
|
|
2993
|
+
return {
|
|
2994
|
+
id: claims.oid,
|
|
2995
|
+
displayName: claims.name,
|
|
2996
|
+
provider: "easyauth",
|
|
2997
|
+
emails: [{ value: claims.email }],
|
|
2998
|
+
username: claims.preferred_username
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
async handleResult(result) {
|
|
3002
|
+
const { profile } = await this.authHandler(result, this.resolverContext);
|
|
3003
|
+
const backstageIdentity = await this.signInResolver(
|
|
3004
|
+
{
|
|
3005
|
+
result,
|
|
3006
|
+
profile
|
|
3007
|
+
},
|
|
3008
|
+
this.resolverContext
|
|
3009
|
+
);
|
|
3010
|
+
return {
|
|
3011
|
+
providerInfo: {
|
|
3012
|
+
accessToken: result.accessToken
|
|
3013
|
+
},
|
|
3014
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
|
|
3015
|
+
profile
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
const easyAuth = createAuthProviderIntegration({
|
|
3020
|
+
create(options) {
|
|
3021
|
+
return ({ resolverContext }) => {
|
|
3022
|
+
var _a;
|
|
3023
|
+
validateAppServiceConfiguration(process.env);
|
|
3024
|
+
if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
|
|
3025
|
+
throw new Error(
|
|
3026
|
+
"SignInResolver is required to use this authentication provider"
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
const authHandler = (_a = options.authHandler) != null ? _a : async ({ fullProfile }) => ({
|
|
3030
|
+
profile: makeProfileInfo(fullProfile)
|
|
3031
|
+
});
|
|
3032
|
+
return new EasyAuthAuthProvider({
|
|
3033
|
+
signInResolver: options.signIn.resolver,
|
|
3034
|
+
authHandler,
|
|
3035
|
+
resolverContext
|
|
3036
|
+
});
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
});
|
|
3040
|
+
function validateAppServiceConfiguration(env) {
|
|
3041
|
+
var _a, _b, _c;
|
|
3042
|
+
if (env.WEBSITE_SKU === void 0) {
|
|
3043
|
+
throw new Error("Backstage is not running on Azure App Services");
|
|
3044
|
+
}
|
|
3045
|
+
if (((_a = env.WEBSITE_AUTH_ENABLED) == null ? void 0 : _a.toLowerCase()) !== "true") {
|
|
3046
|
+
throw new Error("Azure App Services does not have authentication enabled");
|
|
3047
|
+
}
|
|
3048
|
+
if (((_b = env.WEBSITE_AUTH_DEFAULT_PROVIDER) == null ? void 0 : _b.toLowerCase()) !== "azureactivedirectory") {
|
|
3049
|
+
throw new Error("Authentication provider is not Azure Active Directory");
|
|
3050
|
+
}
|
|
3051
|
+
if (((_c = process.env.WEBSITE_AUTH_TOKEN_STORE) == null ? void 0 : _c.toLowerCase()) !== "true") {
|
|
3052
|
+
throw new Error("Token Store is not enabled");
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
|
|
2937
3056
|
const providers = Object.freeze({
|
|
2938
3057
|
atlassian,
|
|
2939
3058
|
auth0,
|
|
@@ -2951,7 +3070,8 @@ const providers = Object.freeze({
|
|
|
2951
3070
|
oidc,
|
|
2952
3071
|
okta,
|
|
2953
3072
|
onelogin,
|
|
2954
|
-
saml
|
|
3073
|
+
saml,
|
|
3074
|
+
easyAuth
|
|
2955
3075
|
});
|
|
2956
3076
|
const defaultAuthProviderFactories = {
|
|
2957
3077
|
google: google.create(),
|
|
@@ -2961,6 +3081,7 @@ const defaultAuthProviderFactories = {
|
|
|
2961
3081
|
okta: okta.create(),
|
|
2962
3082
|
auth0: auth0.create(),
|
|
2963
3083
|
microsoft: microsoft.create(),
|
|
3084
|
+
easyAuth: easyAuth.create(),
|
|
2964
3085
|
oauth2: oauth2.create(),
|
|
2965
3086
|
oidc: oidc.create(),
|
|
2966
3087
|
onelogin: onelogin.create(),
|