Skip to content

Security requirement not serialized correctly #2300

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

Closed
martincostello opened this issue Apr 2, 2025 · 5 comments
Closed

Security requirement not serialized correctly #2300

martincostello opened this issue Apr 2, 2025 · 5 comments
Labels
status:waiting-for-internal-feedback Needs internal stakeholder(s) input to resolve

Comments

@martincostello
Copy link
Contributor

Describe the bug

I'm migrating Swashbuckle.AspNetCore to Microsoft.OpenApi 2.0.0-preview7 (as that's the version used by ASP.NET Core 10 preview 2), and an existing regression test we have for OAuth2 is failing due to the security definition not being deserialized correctly.

Distilling things down, this is what we expect:

{
  "openapi": "3.0.4",
  "info": { },
  "paths": { },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/auth-server/connect/authorize",
            "tokenUrl": "/auth-server/connect/token",
            "scopes": {
              "readAccess": "Access read operations",
              "writeAccess": "Access write operations"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "oauth2": [
        "readAccess",
        "writeAccess"
      ]
    }
  ]
}

However, what we get is this:

{
  "openapi": "3.0.4",
  "info": { },
  "paths": { },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/auth-server/connect/authorize",
            "tokenUrl": "/auth-server/connect/token",
            "scopes": {
              "readAccess": "Access read operations",
              "writeAccess": "Access write operations"
            }
          }
        }
      }
    }
  },
  "security": [
    { }
  ]
}

As far as I can tell, the Target is returning null (so nothing is emitted), but I don't understand why it isn't found via the reference into the host document:

foreach (var securitySchemeAndScopesValuePair in this.Where(static p => p.Key?.Target is not null))

It might be that there's an additional code migration I need to do here for v2, but if there is I haven't been able to spot it.

OpenApi File To Reproduce

[Fact]
public void CanSerializeSecuritySchemes()
{
    var document = new OpenApiDocument();

    document.Components ??= new();
    document.Components.SecuritySchemes.Add("oauth2", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            AuthorizationCode = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri("/auth-server/connect/authorize", UriKind.Relative),
                TokenUrl = new Uri("/auth-server/connect/token", UriKind.Relative),
                Scopes = new Dictionary<string, string>
                {
                    { "readAccess", "Access read operations" },
                    { "writeAccess", "Access write operations" }
                }
            }
        }
    });

    document.SecurityRequirements.Add(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecuritySchemeReference("oauth2", document),
            ["readAccess", "writeAccess"]
        }
    });

    using var writer = new StringWriter();
    var jsonWriter = new OpenApiJsonWriter(writer);
    document.SerializeAsV3(jsonWriter);

    var json = writer.ToString();
}

Expected behavior

The security scheme and the required scopes are serialized to the OpenAPI document.

@RachitMalik12 RachitMalik12 added the status:waiting-for-internal-feedback Needs internal stakeholder(s) input to resolve label Apr 2, 2025
@RachitMalik12
Copy link
Contributor

@MaggieKimani1 can you take a look to see if we can reproduce it? It might have been fixed in the latest preview since preview7 was released in Feb.

@MaggieKimani1
Copy link
Contributor

@RachitMalik12 I've managed to repro it.
As Martin's mentioned, it's because the Target property in this code line is null hence the serialization logic isn't being executed:

foreach (var securitySchemeAndScopesValuePair in this.Where(static p => p.Key?.Target is not null))

Investigating why the Target in the Reference object is not being set.

@MaggieKimani1
Copy link
Contributor

MaggieKimani1 commented Apr 3, 2025

@martincostello the Target value above is not being set since the security scheme object hasn't been registered to the document's workspace, which is where reference resolution takes place.
In order for the reference to be resolved and serialized correctly, you can use this method defined on the document to add a specific component to the components object of the current document as well as register it to the underlying workspace.
Here's the updated code snippet that works for your use case:

[Fact]
public void CanSerializeSecuritySchemes()
{
    var document = new OpenApiDocument();

    document.Components ??= new();
    document.AddComponent("oauth2", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            AuthorizationCode = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri("/auth-server/connect/authorize", UriKind.Relative),
                TokenUrl = new Uri("/auth-server/connect/token", UriKind.Relative),
                Scopes = new Dictionary<string, string>
                {
                    { "readAccess", "Access read operations" },
                    { "writeAccess", "Access write operations" }
                }
            }
        }
    });

    document.Security.Add(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecuritySchemeReference("oauth2", document),
            ["readAccess", "writeAccess"]
        }
    });

    using var writer = new StringWriter();
    var jsonWriter = new OpenApiJsonWriter(writer);
    document.SerializeAsV3(jsonWriter);

    var json = writer.ToString();
}

An alternative would be to add the line below to your test to ensure all components are registered:

document.RegisterComponents();

@martincostello
Copy link
Contributor Author

Thanks for investigating @MaggieKimani1 - is this something that should be added to #2298?

@MaggieKimani1
Copy link
Contributor

MaggieKimani1 commented Apr 3, 2025

Yes I can update the upgrade guide to reflect this. Feel free to close the issue as it's not a bug.

@martincostello martincostello closed this as not planned Won't fix, can't repro, duplicate, stale Apr 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:waiting-for-internal-feedback Needs internal stakeholder(s) input to resolve
Projects
None yet
Development

No branches or pull requests

3 participants