Search code examples
c#azureoauth-2.0swaggerauthorization

Swagger produces "Auth ErrorTypeError: Failed to fetch" when authorizing


I have followed roughly Scott's blog in combination with Joseph's blog. Unfortunately, I get the error Auth ErrorTypeError: Failed to fetch.

The config for authorization is like this ({tenantId} is a valid GUID).

services.AddSwaggerGen(options =>
{
  options.AddSecurityRequirement(new() { ... });
  options.AddSecurityDefinition("oauth2", new()
  {
    Type = SecuritySchemeType.OAuth2,
    Flows = new()
    {
      AuthorizationCode = new()
      {
        AuthorizationUrl = new("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize"),
        TokenUrl = new("https://login.microsoftonline.com/{tenantId}/common/v2.0/token"),
        Scopes = new Dictionary<string, string>
        {
          { "https://graph.microsoft.com/.default", "https://graph.microsoft.com/.default" }            
        }
      }
    }
  });
});

Then, I also have the config for the UI like so ({clientId} is a valid GUID).

app.UseSwaggerUI(options =>
{
  options.OAuthAppName("Beep");
  options.OAuthClientId("{clientId}");
  options.OAuthClientSecret("HakunaMatata");
  options.OAuthScopes(["https://graph.microsoft.com/.default" ]);
  options.OAuthUsePkce();
});

The authorization button in Swagger opens the popup and when requesting the call, the error above is returned. I don't understand where the problem occurs nor how I can troubleshoot it further. The only thing I noticed was that not providing any scopes, leads to an error message in Azure. Providing a scope won't even show the sign-in page there. I've verified that the .well-known produces correct values for authorization and token endpoints. I verified that the secret is correct for the app registration as well as the Redirect URI pointing to my Swagger (https://localhost:7001/swagger/oauth2-redirect.html). Most of the related questions deal with implicit flow or API key, which isn't applicable in my case. Similar questions regarding the error message are rather not rewarding. I also tried to declare a dedicated scope (api://d53180a4-0a79-4f7a-96aa-3f2eed32f559/SwaggerScope) and using it shows me the login page but picking a user produced the same error message.

I inspected the network calls and noticed something unexpected. Apparently, CORS is triggered on the token endpoint and it also reports 404 Not Found (which is self-contradictory but I imagine the not-find-ness is somehow caused by the CORS-ness). The URL is not what I specify in my config above (according to the .well-known endpoint) and I don't control the server to activate the allowed origins.

enter image description here What can I investigate or try out?


Solution

  • I created this project using Swagger UI Authorization for ASP.NET Core API, and it was successfully authorized without any errors.

    Below is the code I used for Swagger UI Authentication.

    builder.Services.AddSwaggerGen(
        o =>
        {
            o.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Swagger Azure Ad", Version = "v1" });
            o.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
            {
                Name = "oauth2.0",
                Type = SecuritySchemeType.OAuth2,
                Flows = new OpenApiOAuthFlows
                {
                    AuthorizationCode = new OpenApiOAuthFlow
                    {
                        AuthorizationUrl = new Uri(builder.Configuration["SwaggerAAD:AuthorizationUrl"]),
                        TokenUrl = new Uri(builder.Configuration["SwaggerAAD:TokenUrl"]),
                        Scopes = new Dictionary<string, string>
                        {
                            {builder.Configuration["SwaggerAAD:Scope"],"Access API as User" }
                        }
                    }
                }
            });
            o.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference=new OpenApiReference{Type=ReferenceType.SecurityScheme,Id="oauth2"}
                    },
                    new []{builder.Configuration["SwaggerAAD:Scope"]}
                }
            });
        });
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI(
            o =>
            {
                o.OAuthClientId(builder.Configuration["SwaggerAAD:ClientId"]);
                o.OAuthUsePkce();
                o.OAuthScopeSeparator(" ");
            });
    }
    

    Below is my complete Program.cs class.

    Program.cs:

    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.Extensions.Options;
    using Microsoft.Identity.Web;
    using Microsoft.OpenApi.Models;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(
        o =>
        {
            o.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Swagger Azure Ad", Version = "v1" });
            o.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
            {
                Name = "oauth2.0",
                Type = SecuritySchemeType.OAuth2,
                Flows = new OpenApiOAuthFlows
                {
                    AuthorizationCode = new OpenApiOAuthFlow
                    {
                        AuthorizationUrl = new Uri(builder.Configuration["SwaggerAAD:AuthorizationUrl"]),
                        TokenUrl = new Uri(builder.Configuration["SwaggerAAD:TokenUrl"]),
                        Scopes = new Dictionary<string, string>
                        {
                            {builder.Configuration["SwaggerAAD:Scope"],"Access API as User" }
                        }
                    }
                }
            });
            o.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference=new OpenApiReference{Type=ReferenceType.SecurityScheme,Id="oauth2"}
                    },
                    new []{builder.Configuration["SwaggerAAD:Scope"]}
                }
            });
        });
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI(
            o =>
            {
                o.OAuthClientId(builder.Configuration["SwaggerAAD:ClientId"]);
                o.OAuthUsePkce();
                o.OAuthScopeSeparator(" ");
            });
    }
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();
    app.Run();
    

    I placed values such as ClientId, TenantId, AuthorizationUrl, and TokenUrl in appsettings.json instead of placing them in program.cs.

    appSettings.json:

    {
      "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "Domain": "<YourDomain>.onmicrosoft.com",
        "TenantId": "<YourTenantID>",
        "ClientId": "<ClientId>",
        "CallbackPath": "/signin-oidc",
        "Scopes": "access_as_user"
      },
      "SwaggerAAD": {
        "AuthorizationUrl": "https://login.microsoftonline.com/<TenantId>/oauth2/v2.0/authorize",
        "TokenUrl": "https://login.microsoftonline.com/<TenantId>/oauth2/v2.0/token",
        "Scope": "https://graph.microsoft.com/.default",
        "ClientId": "<ClientId>"
      },
    

    For the Azure AD API App Registration, the redirect URL is placed in the web application as shown below.

    enter image description here

    For the SwaggerAAD App Registration, I placed the RedirectUrl in the Single-page application as shown below.

    enter image description here

    I took the endpoint values from the Swagger client app registration.

    enter image description here

    I have added the following API permissions below.

    enter image description here

    When I placed authorizationUrl and tokenUrl in Program.cs, I received the error below. So, I put these values in the appsettings.json file.

    enter image description here

    After putting the authorizationUrl and tokenUrl in the appsettings.json file, I received the output below without any errors.

    Output:

    enter image description here

    enter image description here