-
Notifications
You must be signed in to change notification settings - Fork 6.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🐛 Bug Report: [Microsoft Entra ID / Azure DevOps] Can't register an existing component on Azure DevOps #28002
Comments
Hi @bolebon 👋🏻 , Also, is it possible for you to run |
Hi @camilaibs! Sure, here's the frontend stack trace. Here's the backend stack trace:
I ran |
Hello again @camilaibs, I investigated a bit and found this code which seems to be where the issue happens: Actually, when the API call is made, the {
"fullProfile": undefined,
"session": {
"accessToken": "...",
"tokenType": "Bearer",
"scope": "...",
"expiresInSeconds": XXX,
"IdToken": "...",
"refreshToken": "..."
}
} The access token is a JWT, and when decoded you can find a "oid" claim in the payload which is actually the user id in Entra ID so this could be used as a workaround for when the "fullProfile" is not available. |
Hi everyone, I'm coming back with some update regarding this issue. import { jwtDecode } from 'jwt-decode';
import { createBackendModule } from '@backstage/backend-plugin-api';
import { microsoftAuthenticator } from '@backstage/plugin-auth-backend-module-microsoft-provider';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';
export const customMicrosoftAuth = createBackendModule({
// This ID must be exactly "auth" because that's the plugin it targets
pluginId: 'auth',
// This ID must be unique, but can be anything
moduleId: 'custom-microsoft-provider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
// This ID must match the actual provider config, e.g. addressing
// auth.providers.github means that this must be "github".
providerId: 'microsoft',
// Use createProxyAuthProviderFactory instead if it's one of the proxy
// based providers rather than an OAuth based one
factory: createOAuthProviderFactory({
authenticator: microsoftAuthenticator,
async signInResolver(info, ctx) {
const { result } = info;
const { fullProfile, session } = result;
// We take either the full profile ID or the OID from the session
const id = fullProfile?.id ?? jwtDecode<{ oid?: string }>(session.accessToken)?.oid;
if (typeof id !== 'string') {
throw new Error("Microsoft profile contained no valid id");
}
return ctx.signInWithCatalogUser({
annotations: {
"graph.microsoft.com/user-id": id
}
});
},
}),
});
},
});
},
}); And this works perfectly ! |
Hey @bolebon, Thanks for raising this issue! So we use this integration and use it daily, and my team member @andrei-ivanovici contributed the change 👍 We have our Entra App set up like so: We ingest the Users from the groups in Entra, but we allow anyone in our organisation to sign in even if they are not in the catalog. We use a module extension point like you've posted above largely taken from the docs here import { createBackendModule } from '@backstage/backend-plugin-api';
import { microsoftAuthenticator } from '@backstage/plugin-auth-backend-module-microsoft-provider';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory
} from '@backstage/plugin-auth-node';
import {
stringifyEntityRef,
DEFAULT_NAMESPACE
} from '@backstage/catalog-model';
export const customAuth = createBackendModule({
// This ID must be exactly "auth" because that's the plugin it targets
pluginId: 'auth',
// This ID must be unique, but can be anything
moduleId: 'custom-auth-provider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
// This ID must match the actual provider config, e.g. addressing
// auth.providers.github means that this must be "github".
providerId: 'microsoft',
// Use createProxyAuthProviderFactory instead if it's one of the proxy
// based providers rather than an OAuth based one
factory: createOAuthProviderFactory({
authenticator: microsoftAuthenticator,
async signInResolver(info, ctx) {
const {
profile: { email },
} = info;
// Profiles are not always guaranteed to to have an email address.
// You can also find more provider-specific information in `info.result`.
// It typically contains a `fullProfile` object as well as ID and/or access
// tokens that you can use for additional lookups.
if (!email) {
throw new Error('User profile contained no email');
}
// This example resolver simply uses the local part of the email as the name.
const [name, domain] = email.split('@');
// This helper function handles sign-in by looking up a user in the catalog.
// The lookup can be done either by reference, annotations, or custom filters.
// Next we verify the email domain. It is recommended to include this
// kind of check if you don't look up the user in an external service.
if (domain !== 'mycompanydomain.com') {
throw new Error(
`Login failed, this email ${email} does not belong to the expected domain`
);
}
// The helper also issues a token for the user, using the standard group
// membership logic to determine the ownership references of the user.
//
// There are a number of other methods on the ctx, feel free to explore them!
return ctx
.signInWithCatalogUser({
entityRef: { name },
})
.catch(error => {
if (error.name === 'NotFoundError') {
// By using `stringifyEntityRef` we ensure that the reference is formatted correctly
const userEntity = stringifyEntityRef({
kind: 'User',
name: name,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userEntity,
ent: [userEntity],
},
});
}
throw error; // re-throw the error if it's not a UserNotFound error
});
},
}),
});
},
});
},
});
Our setup using this feature works, so if there are any changes please let us know and we'd be happy to collaborate! |
📜 Description
When I try to use the "Register an existing component" feature, I provide the URL of the repository and it manages to get the information of the project but when I click on the "Create PR" button it asks me to login again and, when I do, I get the error:
Cannot read properties of undefined (reading 'id')
👍 Expected behavior
I don't think it should be necessary to login again as I'm already logged in with my Microsoft credentials and even so, it should not return an error and create the PR.
👎 Actual Behavior with Screenshots
When I click on "Create PR" I can see this error appearing in the network logs
Then, this popup appears
And when I click on login, I get this:
👟 Reproduction steps
📃 Provide the context for the Bug.
I'm using AzureDevOps and Microsoft Entra ID integration.
Everything seems to work well, Backstage is capable of getting the list of users and groups coming from Entra ID and is already capable to discover repos having a
catalog-info.yaml
.All the required permissions seems to be granted on the app registration as you can see in the screenshot below.
🖥️ Your Environment
My backstage instance is currently running in an Azure App Service.
👀 Have you spent some time to check if this bug has been raised before?
🏢 Have you read the Code of Conduct?
Are you willing to submit PR?
None
The text was updated successfully, but these errors were encountered: