Merge branch 'backstage:master' into integrations/gitlab-url-reader-token

This commit is contained in:
Captain Fizzbin
2025-04-24 09:57:35 -04:00
committed by GitHub
38 changed files with 1163 additions and 289 deletions
+30
View File
@@ -0,0 +1,30 @@
---
'@backstage/plugin-auth-backend': patch
---
Added the configuration flag `auth.omitIdentityTokenOwnershipClaim` that causes issued user tokens to no longer contain the `ent` claim that represents the ownership references of the user.
The benefit of this new flag is that issued user tokens will be much smaller in
size, but they will no longer be self-contained. This means that any consumers
of the token that require access to the ownership claims now need to call the
`/api/auth/v1/userinfo` endpoint instead. Within the Backstage ecosystem this is
done automatically, as clients will still receive the full set of claims during
authentication, while plugin backends will need to use the `UserInfoService`
which already calls the user info endpoint if necessary.
When enabling this flag, it is important that any custom sign-in resolvers directly return the result of the sign-in method. For example, the following would not work:
```ts
const { token } = await ctx.issueToken({
claims: { sub: entityRef, ent: [entityRef] },
});
return { token }; // WARNING: This will not work with the flag enabled
```
Instead, the sign-in resolver should directly return the result:
```ts
return ctx.issueToken({
claims: { sub: entityRef, ent: [entityRef] },
});
```
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-auth-node': patch
---
Added the `identity` property to `BackstageSignInResult`.
The `prepareBackstageIdentityResponse` function will now also forward the `identity` to the response if present in the provided sign-in result.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-events-backend-module-github': patch
---
Correct README installation instructions.
+92
View File
@@ -0,0 +1,92 @@
# This file contains common fields for all issue templates
# Each field is identified by its ID, and are synced with the `sync.js` script
#
# WARNING: After updating this file, run `yarn sync-issue-templates`
#
body:
- id: issue-labels
type: checkboxes
attributes:
label: '📜 Issue Labels'
options:
- label: 'Please familiarize yourself with the issue labels used in this project: [LABELS.md](https://github.com/backstage/backstage/blob/master/LABELS.md)'
required: true
- id: search-terms
type: textarea
attributes:
label: '🔎 Search Terms'
render: plain
description: |
What search terms did you use when trying to find similar issues?
placeholder: |
List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
e.g. "catalog github rate limited", "http root service configurer", "scaffolder template include", "entity card title"
validations:
required: true
- id: project-area
type: dropdown
attributes:
label: '🗃️ Project Area'
description: Which project area is this issue most closely related to? This will help find an owner for the issue faster.
options:
- Unknown
- Auditor
- Auth
- Catalog
- CLI Tooling
- Core Framework
- Design System
- Documentation
- Events System
- Home
- Kubernetes Plugin
- Notifications
- OpenAPI Tooling
- Permission Framework
- Search
- Software Templates
- TechDocs
validations:
required: true
- type: dropdown
id: integration
attributes:
label: '🔗 External Integration'
description: Is this issue related to an integration with any particular external system?
options:
- N/A
- AWS
- Azure
- Azure DevOps
- Bitbucket Cloud
- Bitbucket Server
- GCP
- Gerrit
- Gitea
- GitHub
- GitLab
- Other
validations:
required: true
- id: read-code-of-conduct
type: checkboxes
attributes:
label: 'Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
- id: willing-to-submit-pr
type: dropdown
attributes:
label: Are you willing to submit a PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Undecided
- Yes, and I have enough information to get started
- Yes, but I would like some more guidance
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
validations:
required: true
+161
View File
@@ -0,0 +1,161 @@
name: '🪲 Report a bug'
description: 'Report a bug that you have encountered'
labels:
- type:bug
- status:needs-triage
body:
- type: markdown
attributes:
value: |
Please fill in each section completely, and [search in GitHub](https://github.com/backstage/backstage/search?type=Issues)
before reporting a new bug to avoid duplicates. Thank you!
# This field is managed by .common.yaml
- id: issue-labels
type: checkboxes
attributes:
label: '📜 Issue Labels'
options:
- label: 'Please familiarize yourself with the issue labels used in this project: [LABELS.md](https://github.com/backstage/backstage/blob/master/LABELS.md)'
required: true
# This field is managed by .common.yaml
- id: search-terms
type: textarea
attributes:
label: '🔎 Search Terms'
render: plain
description: |
What search terms did you use when trying to find similar issues?
placeholder: |
List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
e.g. "catalog github rate limited", "http root service configurer", "scaffolder template include", "entity card title"
validations:
required: true
- type: markdown
attributes:
value: '<br/>' # empty space
- type: markdown
attributes:
value: |
## Defining the bug
Defining the bug accurately will help ensure that it is identified and fixed correctly.
# This field is managed by .common.yaml
- id: project-area
type: dropdown
attributes:
label: '🗃️ Project Area'
description: Which project area is this issue most closely related to? This will help find an owner for the issue faster.
options:
- Unknown
- Auditor
- Auth
- Catalog
- CLI Tooling
- Core Framework
- Design System
- Documentation
- Events System
- Home
- Kubernetes Plugin
- Notifications
- OpenAPI Tooling
- Permission Framework
- Search
- Software Templates
- TechDocs
validations:
required: true
# This field is managed by .common.yaml
- type: dropdown
id: integration
attributes:
label: '🔗 External Integration'
description: Is this issue related to an integration with any particular external system?
options:
- N/A
- AWS
- Azure
- Azure DevOps
- Bitbucket Cloud
- Bitbucket Server
- GCP
- Gerrit
- Gitea
- GitHub
- GitLab
- Other
validations:
required: true
- type: textarea
id: description
validations:
required: true
attributes:
label: '📝 Description & Context'
placeholder: |
Describe the observed behavior that you believe to be incorrect, as well as any additional context that might be useful in understanding the bug.
e.g. "When I navigate to the catalog index page, it loads without any issues. This might be related to https://xkcd.com/1172"
- type: textarea
id: expected-behavior
validations:
required: true
attributes:
label: '👍 Expected Behavior'
placeholder: |
Describe the behavior or result that you expected instead of the observed one.
e.g. "I expect the catalog index page to overheat my CPU"
- type: markdown
attributes:
value: '<br/>' # empty space
- type: markdown
attributes:
value: |
## Reproducing the bug
Being able to easily reproduce the bug will significantly increase the likelihood that it can be identified and fixed. Please provide a repository where the bug can be reproduced, unless this is a critical bug that needs immediate attention.
If you are unable to provide a reproduction repository, please explain the reproduction steps in as much detail as possible, and include the output of `yarn backstage-cli info` if relevant.
If inadequate or no reproduction steps are provided, the bug report is likely to be labeled as needing reproduction steps before it can be progressed further.
- type: input
id: reproduction-repo
attributes:
label: '📦 Reproduction Repo'
description: A link to a GitHub repository where the issue can be reproduced
placeholder: https://github.com/ghost/my-repro-repo
validations:
required: false
- type: textarea
id: steps-to-reproduce
attributes:
label: '🥾 Reproduction steps'
description: 'How do you trigger this bug? Please walk us through it step by step.'
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'"
validations:
required: false
# This field is managed by .common.yaml
- id: read-code-of-conduct
type: checkboxes
attributes:
label: 'Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
- type: dropdown
attributes:
label: Are you willing to submit PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Undecided
- Yes, and I have enough information to get started
- Yes, but I would like some more guidance
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
validations:
required: true
@@ -0,0 +1,97 @@
name: '📖 Report an issue in the documentation'
description: 'Report an issue in the Backstage documentation'
labels:
- domain:docs
- type:documentation
- status:needs-triage
body:
- type: markdown
attributes:
value: |
This template is used to report issues in the Backstage documentation.
The documentation at [backstage.io](https://backstage.io) is only updated when a new release is made. Before reporting an issue here, please verify that the issue also exists in the ["next"](https://backstage.io/docs/next/overview/what-is-backstage) version of the documentation, which you can access using the top-right dropdown menu.
# This field is managed by .common.yaml
- id: issue-labels
type: checkboxes
attributes:
label: '📜 Issue Labels'
options:
- label: 'Please familiarize yourself with the issue labels used in this project: [LABELS.md](https://github.com/backstage/backstage/blob/master/LABELS.md)'
required: true
# This field is managed by .common.yaml
- id: project-area
type: dropdown
attributes:
label: '🗃️ Project Area'
description: Which project area is this issue most closely related to? This will help find an owner for the issue faster.
options:
- Unknown
- Auditor
- Auth
- Catalog
- CLI Tooling
- Core Framework
- Design System
- Documentation
- Events System
- Home
- Kubernetes Plugin
- Notifications
- OpenAPI Tooling
- Permission Framework
- Search
- Software Templates
- TechDocs
validations:
required: true
# This field is managed by .common.yaml
- type: dropdown
id: integration
attributes:
label: '🔗 External Integration'
description: Is this issue related to an integration with any particular external system?
options:
- N/A
- AWS
- Azure
- Azure DevOps
- Bitbucket Cloud
- Bitbucket Server
- GCP
- Gerrit
- Gitea
- GitHub
- GitLab
- Other
validations:
required: true
- type: textarea
id: description
validations:
required: true
attributes:
label: '📝 Description'
description: Describe the problem you have found in the documentation.
placeholder: |
Please provide links to the specific pages and sections that need to be corrected, and describe the problem you have found.
# This field is managed by .common.yaml
- id: read-code-of-conduct
type: checkboxes
attributes:
label: 'Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
- type: dropdown
attributes:
label: Are you willing to submit PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Undecided
- Yes, and I have enough information to get started
- Yes, but I would like some more guidance
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
validations:
required: true
+113
View File
@@ -0,0 +1,113 @@
name: '💡 Suggest a change'
description: 'Suggest an idea for a new feature or change to Backstage'
labels:
- type:suggestion
- status:needs-triage
body:
- type: markdown
attributes:
value: |
This template helps you propose new features, enhancements, or other ideas for Backstage. You can keep your proposal lightweight, but please provide enough detail for reviewers to understand and respond effectively.
For complex additions, consider creating a [Backstage Enhancement Proposal](https://github.com/backstage/backstage/blob/master/beps/README.md) (BEP) instead, as this process streamlines discussions and iteration. Don't worry - you can always convert this issue to a BEP later.
# This field is managed by .common.yaml
- id: issue-labels
type: checkboxes
attributes:
label: '📜 Issue Labels'
options:
- label: 'Please familiarize yourself with the issue labels used in this project: [LABELS.md](https://github.com/backstage/backstage/blob/master/LABELS.md)'
required: true
# This field is managed by .common.yaml
- id: search-terms
type: textarea
attributes:
label: '🔎 Search Terms'
render: plain
description: |
What search terms did you use when trying to find similar issues?
placeholder: |
List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
e.g. "catalog github rate limited", "http root service configurer", "scaffolder template include", "entity card title"
validations:
required: true
# This field is managed by .common.yaml
- id: project-area
type: dropdown
attributes:
label: '🗃️ Project Area'
description: Which project area is this issue most closely related to? This will help find an owner for the issue faster.
options:
- Unknown
- Auditor
- Auth
- Catalog
- CLI Tooling
- Core Framework
- Design System
- Documentation
- Events System
- Home
- Kubernetes Plugin
- Notifications
- OpenAPI Tooling
- Permission Framework
- Search
- Software Templates
- TechDocs
validations:
required: true
- type: textarea
id: need
validations:
required: true
attributes:
label: '🔖 Need'
description: What is the rationale for this change, and who will benefit from it?
placeholder: |
Explain why this change is important and who will benefit from it. Provide context for the change, such as highlighting relevant past work in this area or specific use cases.
- type: textarea
id: proposal
validations:
required: true
attributes:
label: '📝 Proposal'
description: Describe the change that you are suggesting.
placeholder: |
Provide sufficient detail for reviewers to give concrete feedback.
Including a proposed implementation is optional, but if you do, consider adding design details such as TypeScript examples, database schema, or sequence diagrams.
If the change requires particular care when being rolled out, it can be helpful to include a plan for a phased release.
- type: textarea
id: alternatives
attributes:
label: '🔄 Alternatives'
description: Did you consider any other approaches?
placeholder: |
Did you consider any other approaches, and if so, why did you rule them out?
These do not need to be as detailed as the main proposal, but should include enough information to understand the idea and why it wasn't suitable.
# This field is managed by .common.yaml
- id: read-code-of-conduct
type: checkboxes
attributes:
label: 'Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
# This field is managed by .common.yaml
- id: willing-to-submit-pr
type: dropdown
attributes:
label: Are you willing to submit a PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Undecided
- Yes, and I have enough information to get started
- Yes, but I would like some more guidance
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
validations:
required: true
+100
View File
@@ -0,0 +1,100 @@
name: '🚧 Track a maintenance task'
description: 'Track a maintenance task'
labels:
- type:maintenance
- status:needs-triage
body:
- type: markdown
attributes:
value: |
This template helps you track maintenance tasks that require special consideration, such as complex version updates or migrations.
# This field is managed by .common.yaml
- id: issue-labels
type: checkboxes
attributes:
label: '📜 Issue Labels'
options:
- label: 'Please familiarize yourself with the issue labels used in this project: [LABELS.md](https://github.com/backstage/backstage/blob/master/LABELS.md)'
required: true
# This field is managed by .common.yaml
- id: search-terms
type: textarea
attributes:
label: '🔎 Search Terms'
render: plain
description: |
What search terms did you use when trying to find similar issues?
placeholder: |
List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
e.g. "catalog github rate limited", "http root service configurer", "scaffolder template include", "entity card title"
validations:
required: true
# This field is managed by .common.yaml
- id: project-area
type: dropdown
attributes:
label: '🗃️ Project Area'
description: Which project area is this issue most closely related to? This will help find an owner for the issue faster.
options:
- Unknown
- Auditor
- Auth
- Catalog
- CLI Tooling
- Core Framework
- Design System
- Documentation
- Events System
- Home
- Kubernetes Plugin
- Notifications
- OpenAPI Tooling
- Permission Framework
- Search
- Software Templates
- TechDocs
validations:
required: true
- type: textarea
id: task
validations:
required: true
attributes:
label: '🛠️ Task'
description: Describe the task that requires attention
placeholder: |
Explain what needs to be accomplished, including any relevant context such as dependencies or related components.
- type: textarea
id: proposal
validations:
required: true
attributes:
label: '📊 Priority & Impact'
description: Explain the urgency and impact of this task
placeholder: |
Explain what level of prioritization this task should receive. Are there any particular benefits provided in newer versions in case of a version update, or any security vulnerabilities that can be addressed?
Also list any important dates or deadlines, such as current versions reaching end of life, or release dates for stable releases.
# This field is managed by .common.yaml
- id: read-code-of-conduct
type: checkboxes
attributes:
label: 'Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
# This field is managed by .common.yaml
- id: willing-to-submit-pr
type: dropdown
attributes:
label: Are you willing to submit a PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Undecided
- Yes, and I have enough information to get started
- Yes, but I would like some more guidance
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
validations:
required: true
+22
View File
@@ -0,0 +1,22 @@
name: '❓ Other'
description: 'Something not captured by any other template'
body:
- type: markdown
attributes:
value: |
This template can be used to create issues that don't fit any of the other templates. Valid examples include meta issues or other task tracking issues to help coordinate work, or informative issues that are kept for search purposes.
Do not use this template to submit support or help requests, as they will be closed. Please use other forums such as our [Community Discord](https://discord.gg/backstage-687207715902193673) instead.
- type: checkboxes
id: acknowledgement
attributes:
label: '📝 Acknowledgement'
options:
- label: I acknowledge that issues using this template may be closed without further explanation.
required: true
- type: textarea
id: contents
attributes:
label: Content
validations:
required: true
-84
View File
@@ -1,84 +0,0 @@
name: '🐛 Bug Report'
description: 'Submit a bug report to help us improve'
title: '🐛 Bug Report: <title>'
labels:
- bug
body:
- type: markdown
attributes:
value: We value your time and effort to submit this bug report. 🙏
- type: textarea
id: description
validations:
required: true
attributes:
label: '📜 Description'
description: 'A clear and concise description of what the bug is.'
placeholder: 'It bugs out when ...'
- type: textarea
id: expected-behavior
validations:
required: true
attributes:
label: '👍 Expected behavior'
description: 'What did you think should happen?'
placeholder: 'It should ...'
- type: textarea
id: actual-behavior
validations:
required: true
attributes:
label: '👎 Actual Behavior with Screenshots'
description: 'What did actually happen? Add screenshots, if applicable.'
placeholder: 'It actually ...'
- type: textarea
id: steps-to-reproduce
validations:
required: true
attributes:
label: '👟 Reproduction steps'
description: 'How do you trigger this bug? Please walk us through it step by step.'
placeholder:
"Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. Include code or configuration to reproduce, if relevant.\n
1. Go to '...'\n
2. Click on '....'\n
3. Scroll down to '....'"
- type: textarea
id: context
validations:
required: false
attributes:
label: '📃 Provide the context for the Bug.'
description: 'How has this issue affected you? What are you trying to accomplish?'
placeholder: 'Providing context (e.g. links to configuration settings, stack trace or log data) helps us come up with a solution that is most useful in the real world.'
- type: textarea
id: environment
validations:
required: false
attributes:
label: '🖥️ Your Environment'
description: 'Provide Browser Information
Provide Output of `yarn backstage-cli info`'
placeholder: 'Include as many relevant details about the environment you experienced the bug in.'
- type: checkboxes
id: no-duplicate-issues
attributes:
label: '👀 Have you spent some time to check if this bug has been raised before?'
options:
- label: "I checked and didn't find similar issue"
required: true
- type: checkboxes
id: read-code-of-conduct
attributes:
label: '🏢 Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
- type: dropdown
attributes:
label: Are you willing to submit PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Yes I am willing to submit a PR!
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
-54
View File
@@ -1,54 +0,0 @@
name: 🚀 Feature
description: 'Submit a proposal for a new feature'
title: '🚀 Feature: <title>'
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
We value your time and efforts to submit this Feature request form. 🙏
- type: textarea
id: feature-description
validations:
required: true
attributes:
label: '🔖 Feature description'
description: 'A clear and concise description of what the feature is.'
placeholder: 'You should add ...'
- type: textarea
id: context
validations:
required: true
attributes:
label: '🎤 Context'
description: 'Please explain why this feature should be implemented and how it would be used. Add examples, if applicable.'
placeholder: 'In my use-case, ...'
- type: textarea
id: implementation
attributes:
label: '✌️ Possible Implementation'
description: 'A clear and concise description of what you want to happen.'
placeholder: 'Not obligatory, but ideas as to the implementation of the addition or change'
- type: checkboxes
id: no-duplicate-issues
attributes:
label: '👀 Have you spent some time to check if this feature request has been raised before?'
options:
- label: "I checked and didn't find similar issue"
required: true
- type: checkboxes
id: read-code-of-conduct
attributes:
label: '🏢 Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
- type: dropdown
id: willing-to-submit-pr
attributes:
label: Are you willing to submit PR?
description: This is absolutely not required, but we are happy to guide you in the contribution process.
options:
- Yes I am willing to submit a PR!
- No, but I'm happy to collaborate on a PR with someone else
- No, I don't have time to work on this right now
-51
View File
@@ -1,51 +0,0 @@
name: 💬 RFC
description: 'Request For Comments (RFC) from the community'
title: '💬 RFC: <title>'
labels: [rfc]
body:
- type: markdown
attributes:
value: |
We value your time and efforts to submit this RFC form. 🙏
- type: textarea
id: rfc-need
validations:
required: true
attributes:
label: '🔖 Need'
description: 'Let us know why are you proposing this change'
placeholder: 'The problem were trying to address and the benefits/impact we expect to get from this are ...'
- type: textarea
id: proposal
validations:
required: true
attributes:
label: '🎉 Proposal'
description: 'Describe the proposal in as much detail as needed for reviewers to give concrete feedback.'
placeholder: 'Take special care in this section to describe any implications on data privacy or security.'
- type: textarea
id: alternatives
attributes:
label: '〽️ Alternatives'
description: 'What alternatives to the proposed solution were considered?'
placeholder: 'What criteria/data was used to discard these?'
- type: textarea
id: risk
attributes:
label: '❌ Risks'
description: 'What other things happening could conflict or compete (for example for resources) with the proposal?'
placeholder: 'What risk are there and how do we plan to handle them?'
- type: checkboxes
id: no-duplicate-issues
attributes:
label: '👀 Have you spent some time to check if this RFC has been raised before?'
options:
- label: "I checked and didn't find similar issue"
required: true
- type: checkboxes
id: read-code-of-conduct
attributes:
label: '🏢 Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env node
const fs = require('node:fs');
const path = require('node:path');
const yaml = require('yaml');
const TEMPLATES_DIR = path.join(__dirname);
const COMMON_FILE = path.join(TEMPLATES_DIR, '.common.yaml');
// Read the common fields from .common.yaml
const commonDoc = yaml.parseDocument(fs.readFileSync(COMMON_FILE, 'utf8'));
const commonFields = new Map(
commonDoc.get('body').items.map(field => {
field.commentBefore = ' This field is managed by .common.yaml';
return [field.get('id'), field];
}),
);
// Get all YAML files in the templates directory except .common.yaml
const templateFiles = fs
.readdirSync(TEMPLATES_DIR)
.filter(file => file.endsWith('.yaml') && file !== '.common.yaml');
// Process each template file
for (const templateFile of templateFiles) {
const templatePath = path.join(TEMPLATES_DIR, templateFile);
const templateDoc = yaml.parseDocument(fs.readFileSync(templatePath, 'utf8'));
if (templateDoc.get('body')) {
const body = templateDoc.get('body');
for (let i = 0; i < body.items.length; i++) {
const field = body.items[i];
const commonField = commonFields.get(field.get('id'));
if (commonField) {
body.items[i] = commonField;
}
}
// Write the updated template back to file with matching style
fs.writeFileSync(
templatePath,
templateDoc.toString({
indent: 2,
lineWidth: 0,
minContentWidth: 0,
singleQuote: true,
}),
);
console.log(`Updated ${templateFile}`);
}
}
console.log('Sync complete!');
-53
View File
@@ -1,53 +0,0 @@
name: 🦄 UX Component
description: 'For designers to request UX components to be added to the Backstage Storybook'
title: '🦄 UX Component: <title>'
labels: [design]
body:
- type: markdown
attributes:
value: |
We value your time and efforts to submit this RFC form. 🙏
- type: textarea
id: ux-general
validations:
required: true
attributes:
label: '🔖 General'
description: 'Write a nice note to the community requesting the creation of a new component.'
placeholder: 'Include an image of your component. Bonus points for a GIF!'
- type: textarea
id: usage
validations:
required: true
attributes:
label: '💻 Usage'
description: "Tell us what's the point of this component/pattern is."
placeholder: 'How does it help? How should it work? Any rules?'
- type: textarea
id: specs
validations:
required: true
attributes:
label: '📐 Specs'
description: 'Include images that detail the redlines for your component.'
placeholder: "Once we get our Figma workspace set up, we'll be posting the Figma files rather than doing specs by hand."
- type: textarea
id: future
attributes:
label: '🔮 Future'
description: 'List out any upcoming, exciting functionality for this component.'
placeholder: 'The component will have ...'
- type: checkboxes
id: no-duplicate-issues
attributes:
label: '👀 Have you spent some time to check if this UX Component request has been raised before?'
options:
- label: "I checked and didn't find similar issue"
required: true
- type: checkboxes
id: read-code-of-conduct
attributes:
label: '🏢 Have you read the Code of Conduct?'
options:
- label: 'I have read the [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md)'
required: true
+56
View File
@@ -0,0 +1,56 @@
policy:
- section:
- id: ['project-area']
block-list: []
label:
- name: 'area:auditor'
keys: ['Auditor']
- name: 'area:auth'
keys: ['Auth']
- name: 'area:catalog'
keys: ['Catalog']
- name: 'area:core'
keys: ['Core Framework', 'CLI Tooling']
- name: 'area:design-system'
keys: ['Design System']
- name: 'area:documentation'
keys: ['Documentation']
- name: 'area:events'
keys: ['Events System']
- name: 'area:home'
keys: ['Home']
- name: 'area:kubernetes'
keys: ['Kubernetes Plugin']
- name: 'area:notifications'
keys: ['Notifications']
- name: 'area:openapi-tooling'
keys: ['OpenAPI Tooling']
- name: 'area:permission'
keys: ['Permission Framework']
- name: 'area:search'
keys: ['Search']
- name: 'area:scaffolder'
keys: ['Software Templates']
- name: 'area:techdocs'
keys: ['TechDocs']
- id: ['integration']
block-list: []
label:
- name: 'integration:aws'
keys: ['AWS']
- name: 'integration:azure'
keys: ['Azure', 'Azure DevOps']
- name: 'integration:bitbucket-cloud'
keys: ['Bitbucket Cloud']
- name: 'integration:bitbucket-server'
keys: ['Bitbucket Server']
- name: 'integration:gcp'
keys: ['GCP']
- name: 'integration:gerrit'
keys: ['Gerrit']
- name: 'integration:gitea'
keys: ['Gitea']
- name: 'integration:github'
keys: ['GitHub']
- name: 'integration:gitlab'
keys: ['GitLab']
-2
View File
@@ -10,5 +10,3 @@ area:scaffolder:
- '/scaffolder/i'
area:permission:
- '/permission/i'
bep:
- '/\bbep\b/i'
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
for your contributions.
days-before-issue-stale: 60
days-before-issue-close: 7
exempt-issue-labels: plugin,after-vacations,will-fix,bep
exempt-issue-labels: after vacations,no stale
stale-issue-label: stale
stale-pr-message: >
This PR has been automatically marked as stale because it has not had
@@ -37,6 +37,6 @@ jobs:
and we'll re-open the PR so that you can continue the contribution!
days-before-pr-stale: 14
days-before-pr-close: 7
exempt-pr-labels: after-vacations,will-fix
exempt-pr-labels: after vacations,no stale
stale-pr-label: stale
operations-per-run: 100
+18
View File
@@ -20,6 +20,12 @@ jobs:
with:
egress-policy: audit
# We need to checkout the `.github/ISSUE_TEMPLATE` for the advanced labeler action to be able to read the templates
# While at it we might as well checkout all of `.github` so that the labeling actions don't need to fetch their configs
- uses: actions/checkout@v4
with:
sparse-checkout: .github
- name: Add issue labels
uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4
with:
@@ -29,3 +35,15 @@ jobs:
enable-versioned-regex: 0
not-before: 2024-04-19T15:03:51Z
repo-token: '${{ secrets.GITHUB_TOKEN }}'
# These two steps add labels based on user input in the issue form
- name: Parse issue form
uses: stefanbuck/github-issue-parser@v3
id: issue-parser
with:
template-path: .github/ISSUE_TEMPLATE/.common.yaml
- name: Add advanced issue labels
uses: redhat-plumbers-in-action/advanced-issue-labeler@v2
with:
issue-form: ${{ steps.issue-parser.outputs.jsonString }}
token: ${{ secrets.GITHUB_TOKEN }}
+119
View File
@@ -0,0 +1,119 @@
# GitHub Labels in Backstage
This document explains the GitHub labels used in the Backstage main repository. Labels help categorize and track issues and pull requests, but are also used as a communication tool in order to help maintainers respond to issues and pull requests faster.
## Issue Type Labels - `type:*`
These labels help you understand what type of work is needed.
- `type:bug` - Something doesn't work as expected and needs fixing.
- `type:docs` - Documentation is missing or needs updating.
- `type:suggestion` - An proposal for a new feature or change.
- `type:maintenance` - Routine maintenance tasks, version bumps, cleanup, deprecations, etc.
## Priority Labels - `priority:*`
These labels all signal that the issue has been accepted for implementation, and further indicate its priority and whether the owners of the area will address it:
- `priority:critical` - The owners of the area will address this as soon as possible.
- `priority:roadmap` - The owners of the area have this on their roadmap and will address it when possible, some contributions may be welcome.
- `priority:contrib-welcome` - The owners of the area are unlikely to address this in the short term, but contributions are welcome.
- `priority:contrib-needed` - This will not be addressed by the owners of the area, but contributions are welcome.
## Need Labels - `needs:*`
These labels indicate what is needed to move an issue forward before it can be accepted for implementation:
- `needs:bep` - The issue is an advanced addition that needs a [Backstage Enhancement Proposal](./beps/README.md).
- `needs:direction` - The issue needs direction from the owners of the area.
- `needs:more-info` - The issue needs more information from the author.
- `needs:motivation` - It is not clear why this change is needed. The author should provide motivation for the change, for instance by giving examples of concrete use cases or scenarios.
- `needs:repro` - The issue cannot be reproduced by the owners of the area. The author should provide more information to help them reproduce the issue, if possible with a minimal reproduction repository.
- `needs:triage` - The issue needs initial review.
- `needs:unblock` - The issue is blocked by another issue or upstream dependency.
## Area Labels - `area:*`
These labels indicate which part of Backstage an issue or pull request relates to. As a contributor, these help you find issues in areas you're interested in or have expertise in.
- `area:auditor` - Auditor service and it's use in plugins.
- `area:auth` - Authentication and 3rd party authorization.
- `area:catalog` - The Catalog plugin and the Software Catalog model and integrations.
- `area:core` - The core Backstage framework.
- `area:design-system` - The Canon design system and library.
- `area:documentation` - Documentation for adopters, users, and developers.
- `area:events` - The Events system and integrations for other plugins.
- `area:home` - The Home plugin and the main page of the Backstage site.
- `area:kubernetes` - The Kubernetes plugin and integrations for other plugins.
- `area:notifications` - The Notifications plugin and integrations for other plugins.
- `area:openapi-tooling` - The OpenAPI tooling it's use in plugins.
- `area:permission` - The Permissions system and permission integrations from other plugins.
- `area:scaffolder` - The Scaffolder plugin that powers Software Templates.
- `area:search` - The Search plugin and search integrations for other plugins.
- `area:techdocs` - The TechDocs plugin.
## Integration Labels - `integration:*`
These labels help you find issues related to specific external integrations:
- `integration:aws` - [Amazon Web Services](https://aws.amazon.com/)
- `integration:azure` - [Microsoft Azure](https://azure.microsoft.com/) and [Azure DevOps](https://dev.azure.com/)
- `integration:bitbucket-cloud` - [Bitbucket Cloud](https://bitbucket.org/)
- `integration:bitbucket-server` - Bitbucket Server (Stash)
- `integration:gcp` - [Google Cloud Platform](https://cloud.google.com/)
- `integration:gerrit` - [Gerrit](https://www.gerritcodereview.com/)
- `integration:gitea` - [Gitea](https://gitea.com/)
- `integration:github` - [GitHub](https://github.com/)
- `integration:gitlab` - [GitLab](https://gitlab.com/)
## Domain Labels
These labels indicate that an issue is related to specific domains of expertise.
- `domain:a11y` - Web domain issues specifically related to accessibility.
- `domain:design` - Visual design and user experience.
- `domain:docs` - Documentation for adopters, users, and developers.
- `domain:backend` - Backend development in Node.js.
- `domain:tooling` - Tooling and automation in Node.js and GitHub Actions.
- `domain:web` - Frontend development using TypeScript and React.
## General Labels
- `after vacations` - To be handled once the owners return from vacation.
- `do not merge` - The pull request should not be merged.
- `fix before release` - Should be handled before the next release.
- `good first issue` - Good for new contributors.
- `stale` - The issue or pull request has not seen any activity for a while and will be closed if no further activity is seen.
- `no stale` - The issue or pull request should not be closed due to inactivity.
## Common Issue Filters
This is a collection of common issue filters that can help you find issues that you are looking for or that match your interests and skills.
### New Contributors
These issues are ideal for new contributors to get started and don't require much familiarity with Backstage.
- [Backend](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20label%3A%22domain%3Abackend%22%20)
- [Documentation](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20label%3A%22domain%3Adocs%22%20)
- [Tooling](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20label%3A%22domain%3Atooling%22%20)
- [Web](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22good%20first%20issue%22%20label%3A%22domain%3Aweb%22%20)
### Experienced Contributors
These issues generally require some familiarity with Backstage and the codebase, and are either open for or require contributions from the community.
- [Backend](<https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20(label%3Apriority%3Acontrib-welcome%20OR%20label%3Apriority%3Acontrib-needed)%20label%3A%22domain%3Abackend%22%20>)
- [Documentation](<https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20(label%3Apriority%3Acontrib-welcome%20OR%20label%3Apriority%3Acontrib-needed)%20label%3A%22domain%3Adocs%22%20>)
- [Tooling](<https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20(label%3Apriority%3Acontrib-welcome%20OR%20label%3Apriority%3Acontrib-needed)%20label%3A%22domain%3Atooling%22%20>)
- [Web](<https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20(label%3Apriority%3Acontrib-welcome%20OR%20label%3Apriority%3Acontrib-needed)%20label%3A%22domain%3Aweb%22%20>)
### Maintainer Lists
These are useful lists for maintainers.
- [After vacations](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22after%20vacations%22)
- [Critical priority](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22priority%3Acritical%22)
- [Fix before release](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22fix%20before%20release%22)
- [Needs direction](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22needs%3Adirection%22)
- [Needs triage](https://github.com/backstage/backstage/issues?q=is%3Aopen%20is%3Aissue%20label%3A%22needs%3Atriage%22)
+44
View File
@@ -402,6 +402,50 @@ async signInResolver({ profile }, ctx) {
}
```
## Reducing the size of issued tokens
By default the auth backend will issue user identity tokens that include the
ownership references of the user in the `ent` claim of the JWT payload. This is
done to make it easier and more efficient for consumers of the token to resolve
ownership of the user. However, depending on the shape of your organization and
how you resolve ownership claims, these tokens can grow quite large.
To address this, the auth backend now supports the configuration flag
`auth.omitIdentityTokenOwnershipClaim` that causes the `ent` claim to be omitted
from the token. This can be set to `true` in the `app-config.yaml` file.
```yaml title="in app-config.yaml"
auth:
omitIdentityTokenOwnershipClaim: true
```
When this flag is set, the `ent` claim will no longer be present in the token,
and consumers of the token will need to call the `/v1/userinfo` endpoint on the
auth backend to fetch the ownership references of the user. However, there's usually no
action required for consumers. Clients will still receive the full set
of claims during authentication, and any plugin backends will already need to
use the
[`UserInfoService`](../backend-system/core-services/user-info.md) to
access the ownership references from user credentials, which already calls the
user info endpoint if necessary.
When enabling this flag, it is important that any custom sign-in resolvers directly return the result of the sign-in method. For example, the following would not work:
```ts
const { token } = await ctx.issueToken({
claims: { sub: entityRef, ent: [entityRef] },
});
return { token }; // WARNING: This will not work
```
Instead, the sign-in resolver should directly return the result:
```ts
return ctx.issueToken({
claims: { sub: entityRef, ent: [entityRef] },
});
```
## Profile Transforms
Similar to a custom sign-in resolver, you can also write a custom profile transform
@@ -70,7 +70,7 @@ putting all extensions like this in a backend module package of their own in the
`plugins` folder of your Backstage repo:
```sh
yarn new --select backend-module --option pluginId=catalog
yarn new --select backend-plugin-module --option pluginId=catalog
```
The class will have this basic structure:
+3 -1
View File
@@ -55,6 +55,7 @@
"start:microsite": "cd microsite/ && yarn start",
"start:next": "yarn start example-app-next example-backend",
"storybook": "yarn ./storybook run storybook",
"sync-issue-templates": "node ./.github/ISSUE_TEMPLATE/sync.js",
"techdocs-cli": "node scripts/techdocs-cli.js",
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
"test": "NODE_OPTIONS='--no-node-snapshot --experimental-vm-modules' backstage-cli repo test",
@@ -113,7 +114,8 @@
"@backstage/errors": "workspace:^",
"@manypkg/get-packages": "^1.1.3",
"@types/global-agent": "^2.1.3",
"@useoptic/optic": "^1.0.0"
"@useoptic/optic": "^1.0.0",
"yaml": "^2.7.0"
},
"devDependencies": {
"@backstage/cli": "workspace:*",
+9
View File
@@ -43,6 +43,15 @@ export interface Config {
*/
identityTokenAlgorithm?: string;
/**
* Whether to omit the entity ownership references (`ent`) claim from the
* identity token. If this is enabled the `ent` claim will only be available
* via the user info endpoint and the `UserInfoService`.
*
* Defaults to `false`.
*/
omitIdentityTokenOwnershipClaim?: boolean;
/** To control how to store JWK data in auth-backend */
keyStore?: {
provider?: 'database' | 'memory' | 'firestore' | 'static';
+1
View File
@@ -69,6 +69,7 @@
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@backstage/plugin-auth-backend-module-google-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^",
"@types/cookie-parser": "^1.4.2",
"@types/express": "^4.17.6",
"@types/express-session": "^1.17.2",
+123
View File
@@ -17,6 +17,8 @@
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import request from 'supertest';
import { authPlugin } from './authPlugin';
import authModuleGuestProvider from '@backstage/plugin-auth-backend-module-guest-provider';
import { authServiceFactory } from '@backstage/backend-defaults/auth';
describe('authPlugin', () => {
it('should provide an OpenID configuration', async () => {
@@ -42,4 +44,125 @@ describe('authPlugin', () => {
issuer: `http://localhost:${server.port()}/api/auth`,
});
});
describe('mock provider', () => {
const mockProvidersConfig = {
environment: 'test',
providers: {
guest: {
dangerouslyAllowOutsideDevelopment: true,
userEntityRef: 'user:default/tester',
ownershipEntityRefs: [
'group:default/testers',
'group:default/testers2',
],
},
},
};
const expectedIdentity = {
type: 'user',
userEntityRef: 'user:default/tester',
ownershipEntityRefs: ['group:default/testers', 'group:default/testers2'],
};
it('should return tokens with all identity claims by default', async () => {
const { server } = await startTestBackend({
features: [
authPlugin,
authModuleGuestProvider,
authServiceFactory,
mockServices.rootConfig.factory({
data: {
app: {
baseUrl: 'http://localhost',
},
auth: {
...mockProvidersConfig,
},
},
}),
],
});
const refreshRes = await request(server).post('/api/auth/guest/refresh');
expect(refreshRes.status).toBe(200);
expect(refreshRes.body).toMatchObject({
backstageIdentity: {
expiresInSeconds: expect.any(Number),
identity: expectedIdentity,
token: expect.any(String),
},
profile: {},
});
const token = refreshRes.body.backstageIdentity.token;
const decoded = JSON.parse(atob(token.split('.')[1]));
expect(decoded.sub).toEqual(expectedIdentity.userEntityRef);
expect(decoded.ent).toEqual(expectedIdentity.ownershipEntityRefs);
const userInfoRes = await request(server)
.get('/api/auth/v1/userinfo')
.set('Authorization', `Bearer ${token}`);
expect(userInfoRes.status).toBe(200);
expect(userInfoRes.body).toMatchObject({
claims: {
sub: expectedIdentity.userEntityRef,
ent: expectedIdentity.ownershipEntityRefs,
exp: expect.any(Number),
},
});
});
it('should omit ownership claims from the token when the config is set', async () => {
const { server } = await startTestBackend({
features: [
authPlugin,
authModuleGuestProvider,
authServiceFactory,
mockServices.rootConfig.factory({
data: {
app: {
baseUrl: 'http://localhost',
},
auth: {
omitIdentityTokenOwnershipClaim: true,
...mockProvidersConfig,
},
},
}),
],
});
const refreshRes = await request(server).post('/api/auth/guest/refresh');
expect(refreshRes.status).toBe(200);
expect(refreshRes.body).toMatchObject({
backstageIdentity: {
expiresInSeconds: expect.any(Number),
identity: expectedIdentity,
token: expect.any(String),
},
profile: {},
});
const token = refreshRes.body.backstageIdentity.token;
const decoded = JSON.parse(atob(token.split('.')[1]));
expect(decoded.sub).toEqual(expectedIdentity.userEntityRef);
expect(decoded.ent).toBeUndefined();
const userInfoRes = await request(server)
.get('/api/auth/v1/userinfo')
.set('Authorization', `Bearer ${token}`);
expect(userInfoRes.status).toBe(200);
expect(userInfoRes.body).toMatchObject({
claims: {
sub: expectedIdentity.userEntityRef,
ent: expectedIdentity.ownershipEntityRefs,
exp: expect.any(Number),
},
});
});
});
});
@@ -83,7 +83,7 @@ describe('StaticTokenIssuer', () => {
options,
staticKeyStore as unknown as StaticKeyStore,
);
const token = await issuer.issueToken({
const { token } = await issuer.issueToken({
claims: {
sub: entityRef,
ent: [entityRef],
@@ -20,7 +20,10 @@ import { parseEntityRef } from '@backstage/catalog-model';
import { AuthenticationError } from '@backstage/errors';
import { LoggerService } from '@backstage/backend-plugin-api';
import { StaticKeyStore } from './StaticKeyStore';
import { TokenParams } from '@backstage/plugin-auth-node';
import {
BackstageSignInResult,
TokenParams,
} from '@backstage/plugin-auth-node';
const MS_IN_S = 1000;
@@ -56,7 +59,7 @@ export class StaticTokenIssuer implements TokenIssuer {
this.keyStore = keyStore;
}
public async issueToken(params: TokenParams): Promise<string> {
public async issueToken(params: TokenParams): Promise<BackstageSignInResult> {
const key = await this.getSigningKey();
// TODO: code shared with TokenFactory.ts
@@ -81,7 +84,15 @@ export class StaticTokenIssuer implements TokenIssuer {
throw new AuthenticationError('No algorithm was provided in the key');
}
return new SignJWT({ ...additionalClaims, iss, sub, ent, aud, iat, exp })
const token = await new SignJWT({
...additionalClaims,
iss,
sub,
ent,
aud,
iat,
exp,
})
.setProtectedHeader({ alg: key.alg, kid: key.kid })
.setIssuer(iss)
.setAudience(aud)
@@ -89,6 +100,7 @@ export class StaticTokenIssuer implements TokenIssuer {
.setIssuedAt(iat)
.setExpirationTime(exp)
.sign(await importJWK(key));
return { token };
}
private async getSigningKey(): Promise<JWK> {
@@ -60,7 +60,7 @@ describe('TokenFactory', () => {
});
await expect(factory.listPublicKeys()).resolves.toEqual({ keys: [] });
const token = await factory.issueToken({
const { token, identity } = await factory.issueToken({
claims: {
sub: entityRef,
ent: [entityRef],
@@ -68,6 +68,11 @@ describe('TokenFactory', () => {
aud: 'this value will be overridden',
},
});
expect(identity).toEqual({
type: 'user',
userEntityRef: entityRef,
ownershipEntityRefs: [entityRef],
});
const { keys } = await factory.listPublicKeys();
const keyStore = createLocalJWKSet({ keys: keys });
@@ -137,10 +142,10 @@ describe('TokenFactory', () => {
userInfoDatabaseHandler: mockUserInfoDatabaseHandler,
});
const token1 = await factory.issueToken({
const { token: token1 } = await factory.issueToken({
claims: { sub: entityRef },
});
const token2 = await factory.issueToken({
const { token: token2 } = await factory.issueToken({
claims: { sub: entityRef },
});
expect(jwtKid(token1)).toBe(jwtKid(token2));
@@ -159,7 +164,7 @@ describe('TokenFactory', () => {
keys: [],
});
const token3 = await factory.issueToken({
const { token: token3 } = await factory.issueToken({
claims: { sub: entityRef },
});
expect(jwtKid(token3)).not.toBe(jwtKid(token2));
@@ -236,7 +241,7 @@ describe('TokenFactory', () => {
userInfoDatabaseHandler: mockUserInfoDatabaseHandler,
});
const token = await factory.issueToken({
const { token } = await factory.issueToken({
claims: { sub: entityRef, ent: [entityRef] },
});
@@ -29,7 +29,11 @@ import { omit } from 'lodash';
import { DateTime } from 'luxon';
import { v4 as uuid } from 'uuid';
import { LoggerService } from '@backstage/backend-plugin-api';
import { TokenParams, tokenTypes } from '@backstage/plugin-auth-node';
import {
BackstageSignInResult,
TokenParams,
tokenTypes,
} from '@backstage/plugin-auth-node';
import { AnyJWK, KeyStore, TokenIssuer } from './types';
import { JsonValue } from '@backstage/types';
import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler';
@@ -119,6 +123,10 @@ type Options = {
* If not, add a knex migration file in the migrations folder.
* More info on supported algorithms: https://github.com/panva/jose */
algorithm?: string;
/**
* A list of claims to omit from issued tokens and only store in the user info database
*/
omitClaimsFromToken?: string[];
userInfoDatabaseHandler: UserInfoDatabaseHandler;
};
@@ -142,6 +150,7 @@ export class TokenFactory implements TokenIssuer {
private readonly keyStore: KeyStore;
private readonly keyDurationSeconds: number;
private readonly algorithm: string;
private readonly omitClaimsFromToken?: string[];
private readonly userInfoDatabaseHandler: UserInfoDatabaseHandler;
private keyExpiry?: Date;
@@ -153,10 +162,11 @@ export class TokenFactory implements TokenIssuer {
this.keyStore = options.keyStore;
this.keyDurationSeconds = options.keyDurationSeconds;
this.algorithm = options.algorithm ?? 'ES256';
this.omitClaimsFromToken = options.omitClaimsFromToken;
this.userInfoDatabaseHandler = options.userInfoDatabaseHandler;
}
async issueToken(params: TokenParams): Promise<string> {
async issueToken(params: TokenParams): Promise<BackstageSignInResult> {
const key = await this.getKey();
const iss = this.issuer;
@@ -203,7 +213,10 @@ export class TokenFactory implements TokenIssuer {
uip,
};
const token = await new SignJWT(claims)
const tokenClaims = this.omitClaimsFromToken
? omit(claims, this.omitClaimsFromToken)
: claims;
const token = await new SignJWT(tokenClaims)
.setProtectedHeader({
typ: tokenTypes.user.typParam,
alg: key.alg,
@@ -214,7 +227,7 @@ export class TokenFactory implements TokenIssuer {
if (token.length > MAX_TOKEN_LENGTH) {
throw new Error(
`Failed to issue a new user token. The resulting token is excessively large, with either too many ownership claims or too large custom claims. You likely have a bug either in the sign-in resolver or catalog data. The following claims were requested: '${JSON.stringify(
claims,
tokenClaims,
)}'`,
);
}
@@ -225,7 +238,14 @@ export class TokenFactory implements TokenIssuer {
claims: omit(claims, ['aud', 'iat', 'iss', 'uip']),
});
return token;
return {
token,
identity: {
type: 'user',
userEntityRef: sub,
ownershipEntityRefs: ent,
},
};
}
// This will be called by other services that want to verify ID tokens.
+7 -2
View File
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import { TokenParams } from '@backstage/plugin-auth-node';
import {
BackstageUserIdentity,
TokenParams,
} from '@backstage/plugin-auth-node';
/** Represents any form of serializable JWK */
export interface AnyJWK extends Record<string, string> {
@@ -31,7 +34,9 @@ export type TokenIssuer = {
/**
* Issues a new ID Token
*/
issueToken(params: TokenParams): Promise<string>;
issueToken(
params: TokenParams,
): Promise<{ token: string; identity?: BackstageUserIdentity }>;
/**
* List all public keys that are currently being used to sign tokens, or have been used
@@ -77,8 +77,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
) {}
async issueToken(params: TokenParams) {
const token = await this.tokenIssuer.issueToken(params);
return { token };
return await this.tokenIssuer.issueToken(params);
}
async findCatalogUser(query: AuthResolverCatalogUserQuery) {
@@ -147,13 +146,12 @@ export class CatalogAuthResolverContext implements AuthResolverContext {
entity,
);
const token = await this.tokenIssuer.issueToken({
return await this.tokenIssuer.issueToken({
claims: {
sub: stringifyEntityRef(entity),
ent: ownershipEntityRefs,
},
});
return { token };
}
async resolveOwnershipEntityRefs(
@@ -101,6 +101,11 @@ export async function createRouter(
tokenFactoryAlgorithm ??
config.getOptionalString('auth.identityTokenAlgorithm'),
userInfoDatabaseHandler,
omitClaimsFromToken: config.getOptionalBoolean(
'auth.omitIdentityTokenOwnershipClaim',
)
? ['ent']
: [],
});
}
+2 -3
View File
@@ -104,9 +104,7 @@ export type AuthResolverCatalogUserQuery =
// @public
export type AuthResolverContext = {
issueToken(params: TokenParams): Promise<{
token: string;
}>;
issueToken(params: TokenParams): Promise<BackstageSignInResult>;
findCatalogUser(query: AuthResolverCatalogUserQuery): Promise<{
entity: Entity;
}>;
@@ -126,6 +124,7 @@ export interface BackstageIdentityResponse extends BackstageSignInResult {
// @public
export interface BackstageSignInResult {
identity?: BackstageUserIdentity;
token: string;
}
@@ -44,6 +44,30 @@ describe('prepareBackstageIdentityResponse', () => {
});
});
it('uses the identity in the result if present', () => {
jest.spyOn(Date, 'now').mockReturnValue(5000);
const token = mkToken({ sub: 'k:ns/n', ent: ['k:ns/o'], exp: 1005 });
expect(
prepareBackstageIdentityResponse({
token,
identity: {
type: 'user',
userEntityRef: 'k:ns/other',
ownershipEntityRefs: ['k:ns/group1', 'k:ns/group2'],
},
}),
).toEqual({
token,
expiresInSeconds: 1000,
identity: {
type: 'user',
userEntityRef: 'k:ns/other',
ownershipEntityRefs: ['k:ns/group1', 'k:ns/group2'],
},
});
});
it('should reject tokens without subject', () => {
const token = mkToken({});
expect(() =>
@@ -57,7 +57,7 @@ export function prepareBackstageIdentityResponse(
return {
...result,
expiresInSeconds: exp,
identity: {
identity: result.identity ?? {
type: 'user',
userEntityRef: sub,
ownershipEntityRefs: ent,
+7 -1
View File
@@ -34,6 +34,12 @@ export interface BackstageSignInResult {
* The token used to authenticate the user within Backstage.
*/
token: string;
/**
* Identity information to pass to the client rather than using the
* information that's embeeded in the token.
*/
identity?: BackstageUserIdentity;
}
/**
@@ -141,7 +147,7 @@ export type AuthResolverContext = {
/**
* Issues a Backstage token using the provided parameters.
*/
issueToken(params: TokenParams): Promise<{ token: string }>;
issueToken(params: TokenParams): Promise<BackstageSignInResult>;
/**
* Finds a single user in the catalog using the provided query.
+5 -16
View File
@@ -27,16 +27,14 @@ Please find all possible webhook event types at the
yarn --cwd packages/backend add @backstage/plugin-events-backend-module-github
```
### Event Router
```ts
// packages/backend/src/index.ts
import { eventsModuleGithubEventRouter } from '@backstage/plugin-events-backend-module-github/alpha';
// ...
backend.add(eventsModuleGithubEventRouter);
backend.add(import('@backstage/plugin-events-backend-module-github'));
```
#### Legacy Backend System
### Legacy Backend System
#### Event Router
```ts
// packages/backend/src/plugins/events.ts
@@ -44,16 +42,7 @@ const eventRouter = new GithubEventRouter({ events: env.events });
await eventRouter.subscribe();
```
### Signature Validator
```ts
// packages/backend/src/index.ts
import { eventsModuleGithubWebhook } from '@backstage/plugin-events-backend-module-github/alpha';
// ...
backend.add(eventsModuleGithubWebhook);
```
#### Legacy Backend System
#### Signature Validator
Add the signature validator for the topic `github`:
+2
View File
@@ -5477,6 +5477,7 @@ __metadata:
"@backstage/config": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-auth-backend-module-google-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/plugin-catalog-node": "workspace:^"
"@backstage/types": "workspace:^"
@@ -43610,6 +43611,7 @@ __metadata:
sort-package-json: "npm:^2.8.0"
typedoc: "npm:^0.27.6"
typescript: "npm:~5.6.0"
yaml: "npm:^2.7.0"
languageName: unknown
linkType: soft