Skip to content

Conversation

@eliykat
Copy link
Member

@eliykat eliykat commented Nov 27, 2025

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-28808

📔 Objective

We have a swagger.json and SwaggerUI hosted on our Help Center for the public API: https://bitwarden.com/help/api/.

However it currently has the following issues:

  • only supports targeting the US server - not EU
  • the identity endpoint is set to localhost - so the Authorize button doesn't work and you can't use the "Try it out" functionality

This PR makes the following changes:

  • in SwaggerGen configuration - configure US and EU servers and corresponding authorization options. This is the default configuration that will be used to produce the swagger.json build artifact, which is used by the Help Center.
    • note: you cannot automatically associate the server with the authorization option (or otherwise parameterize it etc) so the best we can do is try to clearly explain which authorization to use
    • this should be permitted by CORS because our cloud servers permit requests from the product website: ref.
  • in the API project Swagger configuration - override the default config with the details of the locally running server. This will be used for development servers and self-host servers.

We used to specify the --host option in generate_openapi_files.ps1 but that is no longer needed (and in any case didn't work because you can't specify the authorization config from the command line).

I think this approach is reasonable but I am open to other ways to achieve this. Platform should probably be the one to review this.

📸 Screenshots

Preview of how it should look on the Help Center:

Screen.Recording.2025-11-27.at.1.03.51.pm.mov

Preview of how it should look in dev and self hosted:

Screen.Recording.2025-11-27.at.1.06.04.pm.mov

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

- in generated JSON (used in help center), only show cloud options
- in self-host and dev, only show local option

Mixing these will cause CORS issues because server is not expecting
requests from those origins.
@eliykat eliykat requested a review from justindbaur November 27, 2025 02:48
@claude
Copy link
Contributor

claude bot commented Nov 27, 2025

Claude finished @eliykat's task —— View job


Code Review Complete

I've reviewed the changes to fix the invalid identity URL in Swagger. The implementation is solid and addresses the stated problem. Here are my findings:


Summary

This PR successfully configures Swagger to support both US and EU cloud servers in the generated swagger.json artifact for the Help Center, while dynamically overriding to local configuration for development and self-hosted environments. The approach elegantly balances the needs of different deployment scenarios.


Findings

Finding 1: Missing unit tests for new extension method (⚠️)

The new AddSwaggerServerWithSecurity extension method in src/SharedWeb/Utilities/ServiceCollectionExtensions.cs:1081-1128 lacks test coverage, which contributes to the 35 missing lines reported by Codecov. While Swagger configuration is challenging to test, the method contains non-trivial logic (URI parsing, dictionary construction, OpenAPI object creation) that would benefit from verification.

Suggested test approach

Create a test class SharedWeb.Test/Utilities/ServiceCollectionExtensionsTests.cs that:

  • Verifies servers are added with correct URLs and descriptions
  • Confirms security definitions are created with proper token URLs
  • Validates security requirements reference the correct scheme IDs
  • Checks that multiple calls add multiple server configurations

Reference the existing SwaggerDocUtil.cs test utility for SwaggerGen setup patterns.

Finding 2: Codecov reports 35 lines missing coverage in Startup.cs (💭)

The PreSerializeFilters code in src/Api/Startup.cs:304-344 shows as untested. This is expected since:

  • This code only runs in Development/SelfHosted environments
  • The filters execute at runtime when serving swagger.json
  • The CI pipeline generates swagger artifacts using Development environment but doesn't exercise the filter execution path

Consider whether integration tests that actually fetch the swagger.json endpoint would be valuable, though this may be excessive for configuration code.

Finding 3: Potential confusion between security schemes for end users (💭)

With multiple servers (US/EU) and multiple security definitions (US_server/EU_server), users must manually match the server selection with the correct authorization option. The PR description acknowledges this limitation with the note: "you cannot automatically associate the server with the authorization option."

The added description text helps ("Use this option if you've selected the {serverDescription}" and "Note: your authorization must match the server you have selected"), but users could still select US server + EU authorization.

This appears to be a limitation of OpenAPI/Swagger UI rather than the implementation. The current approach with clear descriptions is reasonable given the constraints.

Finding 4: Security scheme clearing in PreSerializeFilters is correct (👍)

The swaggerDoc.Components.SecuritySchemes.Clear() and swaggerDoc.SecurityRequirements.Clear() in src/Api/Startup.cs:312,329 properly remove cloud server configurations for local environments, ensuring development/self-hosted deployments don't expose cloud identity URLs.


Security Considerations

✅ No security issues identified. The changes:

  • Don't expose sensitive data
  • Correctly configure OAuth2 endpoints for different environments
  • Properly isolate cloud vs. local configurations
  • Are read-only documentation changes that don't affect authentication logic

Response to Previous Review Feedback

The author (eliykat) correctly explained why dani-garcia's simplification suggestion wouldn't work: the CI pipeline uses Development environment for swagger generation but needs to output cloud server configurations. The PreSerializeFilters approach is the correct solution to override configuration based on runtime environment.


Code Quality Observations

✅ Method naming: AddSwaggerAddSwaggerGen better reflects that it configures SwaggerGen
✅ Documentation: XML comments and inline comments clearly explain the dual-configuration strategy
✅ Separation of concerns: Extension method in SharedWeb makes it reusable
✅ Code organization: Clear comments distinguish cloud vs. local configuration sections


Recommendations

  1. Add unit tests for AddSwaggerServerWithSecurity to improve coverage and prevent regressions (⚠️ priority)
  2. Consider documenting the server/auth matching requirement in Help Center docs when this is deployed
  3. The codecov coverage drop is acceptable given the nature of the code (runtime filters in development-only paths)

Action Items for Author

  • Add unit tests for AddSwaggerServerWithSecurity extension method
  • (Optional) Coordinate with docs team about server/authorization matching guidance

Overall this is a well-implemented solution that properly addresses the issue within the constraints of the Swagger/OpenAPI specification. The approach of configuring cloud servers by default and overriding for local environments is sound.

@eliykat eliykat requested review from a team and djsmith85 November 27, 2025 02:49
@github-actions
Copy link
Contributor

github-actions bot commented Nov 27, 2025

Logo
Checkmarx One – Scan Summary & Detailsdb911c74-f57c-491d-a1d3-7e59bd30a469

New Issues (2)

Checkmarx found the following issues in this Pull Request

Severity Issue Source File / Package Checkmarx Insight
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 1519
detailsMethod at line 1519 of /src/Api/Vault/Controllers/CiphersController.cs gets a parameter from a user request from id. This parameter value flows ...
ID: dMGF5qNfAN72zlvQcA1MgbhHv%2Fc%3D
Attack Vector
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 1395
detailsMethod at line 1395 of /src/Api/Vault/Controllers/CiphersController.cs gets a parameter from a user request from id. This parameter value flows ...
ID: iOCFr11iI9znjDnv46yLfiS4aDY%3D
Attack Vector
Fixed Issues (2)

Great job! The following issues were fixed in this Pull Request

Severity Issue Source File / Package
MEDIUM CSRF /src/Api/KeyManagement/Controllers/AccountsKeyManagementController.cs: 97
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 300

@eliykat eliykat changed the title [BEEEP] [PM-28808] Swagger has invalid identity URL [BEEEP] [PM-28808] Fix invalid identity URL in Swagger Nov 27, 2025
@codecov
Copy link

codecov bot commented Nov 27, 2025

Codecov Report

❌ Patch coverage is 62.76596% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.39%. Comparing base (89a2eab) to head (2355aec).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/Api/Startup.cs 14.63% 35 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6653      +/-   ##
==========================================
- Coverage   53.41%   53.39%   -0.03%     
==========================================
  Files        1911     1911              
  Lines       85129    85190      +61     
  Branches     7642     7642              
==========================================
+ Hits        45469    45483      +14     
- Misses      37909    37956      +47     
  Partials     1751     1751              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dani-garcia
dani-garcia previously approved these changes Nov 27, 2025
Copy link
Member

@dani-garcia dani-garcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, can confirm that the generated schemas work and match what's expected. I've also checked that the SDK works when the schema has multiple servers defined and besides some code duplication that I fixed in bitwarden/sdk-internal#589, everything works great.

I've left a non-blocking suggestion on a possible way to remove the manual modification of the servers list, I've tested it locally and it seems to do the trick but I may have missed some corner case.

// Configure Bitwarden cloud US and EU servers. These will appear in the swagger.json build artifact
// used for our help center. These are overwritten with the local server when running in self-hosted
// or dev mode (see Api Startup.cs).
config.AddSwaggerServerWithSecurity(
Copy link
Member

@dani-garcia dani-garcia Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it wouldn't be easier to change this code to be:

if (globalSettings.SelfHosted)
{
    // Configure self-hosted server
    config.AddSwaggerServerWithSecurity(
        serverId: "self_hosted_server",
        serverUrl: globalSettings.BaseServiceUri.Api,
        identityTokenUrl: $"{globalSettings.BaseServiceUri.Identity}/connect/token",
        serverDescription: "Self-hosted");
}
else
{
    // Configure Bitwarden cloud US and EU servers. These will appear in the swagger.json build artifact
    // used for our help center. These are overwritten with the local server when running in self-hosted
    // or dev mode (see Api Startup.cs).
    config.AddSwaggerServerWithSecurity(
        serverId: "US_server",
        serverUrl: "https://api.bitwarden.com",
        identityTokenUrl: "https://identity.bitwarden.com/connect/token",
        serverDescription: "US server");

    config.AddSwaggerServerWithSecurity(
        serverId: "EU_server",
        serverUrl: "https://api.bitwarden.eu",
        identityTokenUrl: "https://identity.bitwarden.eu/connect/token",
        serverDescription: "EU server");
}

And then remove the manual modification of the servers that is currently being done using config.PreSerializeFilters in Startup.Cs. Seems to generate the same servers in the schema when run locally.

dani-garcia added a commit to bitwarden/sdk-internal that referenced this pull request Nov 27, 2025
## 🎟️ Tracking

<!-- Paste the link to the Jira or GitHub issue or otherwise describe /
point to where this change is coming from. -->

## 📔 Objective

The server bindings template iterates over all auth methods of all the
servers and generates code for each of them. This means that if the
schema has two servers defined (like the US and EU servers) it will
generate code like this:
```rust
        if let Some(ref token) = config.oauth_access_token {
            request = request.bearer_auth(token.to_owned());
        };
        // This one is just duplicate
        if let Some(ref token) = config.oauth_access_token {
            request = request.bearer_auth(token.to_owned());
        };
```

This won't break anything and is safe to do, but it's kind of wasteful.
Instead, we can just use the first auth method only.

The change doesn't have any effect on the bindings for the moment as
we're only ever defining one server and one auth method, but
bitwarden/server#6653 will add the US and EU
servers to the server API schema, which will result in the above shown
duplication.

## 🚨 Breaking Changes

<!-- Does this PR introduce any breaking changes? If so, please describe
the impact and migration path for clients.

If you're unsure, the automated TypeScript compatibility check will run
when you open/update this PR and provide feedback.

For breaking changes:
1. Describe what changed in the client interface
2. Explain why the change was necessary
3. Provide migration steps for client developers
4. Link to any paired client PRs if needed

Otherwise, you can remove this section. -->

## ⏰ Reminders before review

- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or
informed the documentation
  team

## 🦮 Reviewer guidelines

<!-- Suggested interactions but feel free to use (or not) as you desire!
-->

- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry
that's not quite a confirmed
  issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or
concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or
indications of technical debt
- ⛏ (`:pick:`) for minor or nitpick changes
@eliykat
Copy link
Member Author

eliykat commented Nov 28, 2025

Thanks @dani-garcia ! I did mean to ask about how this would affect the SDK so thank you for checking that.

I tried your suggestion, but the pipeline generates the swagger jsons using the development environment configuration which means it outputs the localhost configuration. If I change that to using Production, it fails because no server certificates are configured; using the Development environment seems like a way to bypass the configuration required for a real server. Maybe that in itself needs to be revisited (do we need a CI environment type? not sure) but I thought that was out of scope for this PR.

@eliykat eliykat requested review from dani-garcia and removed request for justindbaur November 28, 2025 01:13
@eliykat eliykat enabled auto-merge (squash) November 28, 2025 22:00
@eliykat eliykat disabled auto-merge November 28, 2025 22:00
@eliykat eliykat added needs-qa and removed needs-qa labels Nov 28, 2025
@eliykat eliykat enabled auto-merge (squash) December 2, 2025 23:12
@eliykat eliykat merged commit ee26a70 into main Dec 2, 2025
34 of 35 checks passed
@eliykat eliykat deleted the ac/pm-28808/swagger-download-has-invalid-identity-tokenurl branch December 2, 2025 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants