Backstage by Example (Part 2)
We continue to configure our Backstage App; ending up with a sensible authentication and authorization solution.
This is article is part of a series starting with Backstage by Example (Part 1).
Clearing Out Sample Data
Having finished up working through the two Backstage Getting Started documents, we are left with a Backstage App with sample data (entities) that we clearly do not want to carry forward.
The first steps are to delete the two Components that we created: demo and tutorial. We do this by navigating to the component and using the menu (upper-right) item Unregister entity.
One interesting side effect of unregistering the Components is that the associated Location Entities (URLs) are also unregistered.
For completeness, we delete the GitHub Repository backing the tutorial Component.
At this point, however, we still have the initial sample data (entities) in our Backstage App. To get rid of most of the sample data (we will leave the Documentation Template Template in place for now), we update app-config.yaml as follows and delete the catalog-info.yaml file.
Starting the Backstage App with these changes, we can see that the sample data is no longer present; include Users and Groups. At this point there are only two entities: the Documentation Template Template and the Location associated with it.
Users and Groups
We next look to ingest User and Group entities from our GitHub Organization’s People and Teams respectively.
The Backstage catalog can be set up to ingest organizational data — users and teams — directly from an organization in GitHub or GitHub Enterprise. The result is a hierarchy of User and Group kind entities that mirror your org setup.
We first need to update the GitHub App permissions.
If Backstage is configured to use GitHub Apps authentication you must grant Read-Only access for Members under Organization in order to ingest users correctly. You can modify the app’s permissions under the organization settings,
We then update app-config.yaml; adding a github-org type. The documentation neglects to include the required allow rules.
Since I am using a new GitHub Organization, it only has a single Person; my GitHub account.
Once we start the Backstage App, we find that the Person is ingested as a User entity.
Similarly we create a GitHub Organization Team; my-team.
Within a few seconds the Team is ingested as a Group entity.
Authentication and Users
It is interesting to observe that our authentication and User ingestion mechanisms are independent of each other. One might wonder if the Backstage App authentication of a GitHub account matches up with the same Backstage App User created from that account as a Member of a GitHub Organization. More simply, when logged in, am I the similarly named Backstage User.
To validate, we create a Component and observe that by setting the ownership to the my-team Group, the Component shows as being owned by us.
Authorization
We were able to authenticate into the Backstage App before we ingested Users and Groups from a GitHub Organization. This suggests that any GitHub account can authenticate (and thus be authorized) to use this Backstage App. To validate the problem, we can use another GitHub account that is not associated with our GitHub Organization and indeed we can authenticate to our Backstage App and then perform any operations as we please.
How can we manage authorization? As of late 2021, the answer is that Backstage has no built-in authorization system.
Backstage has many different authentication methods, but has no built-in way to authorize access to specific data, APIs, or interface actions. Authorization is a way to enforce rules about what actions are allowed for a given user of a system. In Backstage today, endpoints are not protected and all actions are available to anyone.
— [PRFC] Backstage permissions and authorization
We, however, will need to at minimum restrict which GitHub accounts can access the Backstage App.
Pouring through the Backstage documentation, I found the following gem.
AuthHandler
Similar to a custom sign-in resolver, you can also write a custom auth handler function which is used to verify and convert the auth response into the profile that will be presented to the user. This is where you can customize things like display name and profile picture.This is also the place where you can do authorization and validation of the user and throw errors if the user should not be allowed access in Backstage.
After a bunch of tracing through the source code and debugging, I found myself with the following code that determines if the GitHub account that is logging in is associated with a User entity that was ingested from the GitHub Organization.
packages/backend/src/plugins/auth.ts
And indeed now, after starting the Backstage App, when we use another GitHub account that is not associated with our GitHub Organization we are now not authorized and even get a sensible user experience.
Please note: When I first saw that pattern of having to edit code to configure the Backstage App, I was a bit horrified. Having used it more, I realized that is an elegant way of supporting a complex mix of optional features.
Next Steps
In the next article, Backstage by Example (Part 3), we explore how to deploy our Backstage App to production.