I am working on Azure AD and .net core application. I have a swagger application which will do authentication and authorization. Below is my swagger config.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = swaggerUIOptions.AuthorizationUrl,
TokenUrl = swaggerUIOptions.TokenUrl,
Scopes = new Dictionary<string, string>
{
{ "Read", "https://graph.microsoft.com/user.read" }
}
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new [] { "readAccess", "writeAccess" } }
});
});
Below is my config in Configure.
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "swagger";
c.OAuthClientId(swaggerUIOptions.ClientId);
c.OAuthClientSecret(swaggerUIOptions.ClientSecret);
c.OAuthRealm(azureActiveDirectoryOptions.ClientId);
c.OAuthAppName("Swagger");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
Below are my AuthorizationUrl and TokenUrl
"AuthorizationUrl": "https://login.microsoftonline.com/tenantid/oauth2/v2.0/authorize",
"TokenUrl": "https://login.microsoftonline.com/tenantid/oauth2/v2.0/token"
Below is API permission set in azure ad.
When I try to get token(I selected scope checkmark) in swagger I get below error.
The application swagger asked for Read that doesn't exist on the resource
Also, I have one more app for back end APIs. When we try to get access token, can we the same token to call Graph APIs and backend APIs?
The application swagger asked for Read that doesn't exist on the
resource.
There is no Read permission under Microsoft Graph api. You can use User.Read or https://graph.microsoft.com/User.Read
Also, I have one more app for back end APIs. When we try to get access
token, can we the same token to call Graph APIs and backend APIs?
You can use this token to call Graph APIs, but need to add the permissions for the api. You can not use the same token to call different apis under different resources.
Related
I want to implement the OAuth 2.0 in my web application. In web application, I have used swagger and I implemented the following code.
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://myWebAppUrl", UriKind.Absolute),
TokenUrl = new Uri("https://myWebAppUrl"),
}
}
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
},
new[] { "readaccess", "writeaccess" }
}
});
I added the above code and ran my project.
Then, On the click of Authorize I was able to see the "Authorize" button and When I clicked on "Authorize" button, I was redirected to my web app login page.
Now,
I want to validate the login credentials that the user will enter on the login page, So, I generated JWT token in my login API to validate the user's credentials.
I may have missed some steps here.
But, I am lost how to redirect back to the "Swagger Documentation" after the login is successful.
After the login is successful it redirects me to the dashboard (I don't want this).
Please help how can I proceed.
If you are using Identity Server than you can create additional client for swagger and set redirect uri to swagger. You can start with tutorial.
I have implemented authentication for my ASP.NET Core 3.0 Web API using Azure AD. When I use the [Authorize] attribute, I am getting a http 401 error response with the message
Bearer error="invalid_token", error_description="The signature key was not found"
My current ConfigureService() in Startup.cs looks like this:
options.AddSecurityDefinition("oauth", new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
Implicit = new OpenApiOAuthFlow()
{
TokenUrl = new Uri($"https://login.microsoftonline.com/<mytenantid>/oauth2/v2.0/token"),
AuthorizationUrl = new Uri($"https://login.microsoftonline.com/TenantId/oauth2/v2.0/authorize", UriKind.RelativeOrAbsolute),
Scopes = { { "api://<myappid>/user_impersonation", "user_impersonation" } }
}
}
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.Authority = $"https://login.microsoftonline.com/<mytenantid>/v2.0";
o.Audience = "<myappid>";
o.TokenValidationParameters.ValidAudiences = new string[] { "<myappid>", $"api://<myappid>" };
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
Please let me know if I am missing something
I have answered many similar questions before. According to your question, you expose the web api protected by Azure. Next, you need to create a client application and use the client application to call the api application.
Usually the 401 error means that the audience of your token does not match your api. When you use the token to call the api, you will receive a 401 unauthorized error. The access token is issued based on the audience, so you must Make sure to set the scope to your api when you request the token. Of course you can also parse the token, check the aud claim, and make sure it is the api you want to call.
I use the auth code flow to do a simple demonstration for you:
First expose the api of the api application and add the client application.
Next,under 'API permissions', give your front-end application access to your back-end api application:
Under 'API permissions' click on 'Add permission', then click on the 'My APIs' tab.
Find your backend application and select the appropriate scope.
Click 'Add permissions'.
Grant admin consent for your APIs.
Get token:
Parse the token:
I have a .net core 3.1 web API. I want to secure the endpoints using bearer tokens from Azure AD.
I have followed This guide. I have a registered an app for the API and an app for the client.
In my API, I have added a registration for the jwt (as outlined in the code)
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.Audience = Configuration["AzureAd:ResourceId"];
opt.Authority = $"{Configuration["AzureAd:Instance"]}{Configuration["AzureAd:TenantId"]}";
});
And my configuration section for the API looks like this
"AzureAd": {
"ResourceId": "api://<client-id-guid>",
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id-guid>"
}
where the client-id-guid is the client id of the api application (used the default configuration for the api:// address).
Then, for my client, I implemented a test in a new solution to get the token and call a secured endpoint.
AadConfiguration config = AadConfiguration.ReadFromJsonFile("appsettings.json");
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority))
.Build();
string[] ResourceIds = new string[] {config.ResourceID};
var token = await app.AcquireTokenForClient(ResourceIds)
.ExecuteAsync();
Assert.IsFalse(string.IsNullOrWhiteSpace(token.AccessToken));
var request = new RestRequestBuilder("https://my-api-staging.azurewebsites.net/api")
.WithSegments("health-check", "auth")
.WithBearerToken(token.AccessToken)
.Build();
try
{
var response = await _restHelper.GetJsonAsync<dynamic>(request);
Assert.IsFalse(string.IsNullOrEmpty(response?.serverTime?.ToString()));
}
catch (RestException e)
{
Assert.IsFalse(true, e.ResponseContent);
}
And the configuration for this test
{
"Instance": "https://login.microsoftonline.com/{0}",
"TenantId": "<tenant-id-guid>",
"ClientId": "<client-id-guid>",
"ClientSecret": "<client-secret>",
"ResourceId": "api://<api-client-id-guid>/.default"
}
Tenant Id is the sale for both the API and the test application
The client ID is the client id for the client app registration, and its secret
The resource Id is the API's application id, which is the api:// with the api's client Id
I can retrieve the access token just fine. However, when I call an endpoint using the access token I am given, I get the following error:
Couldn't find a valid certificate with subject 'CN=TODO.azurewebsites.net' on the 'CurrentUser\My'
I'm not sure why my authentication is failing when I have followed the steps in the document.
I can retrieve the access token just fine. However, when I call an endpoint using the access token I am given, I get the following error:
I follow the guide and test in my site which works very well.
After getting access token, you can try to use it in postman to send request to core api. While the core api is 3.1 version, so it do not have /api to route it.
And you can use HttpClient to call core webapi.
var token = await app.AcquireTokenForClient(ResourceIds).ExecuteAsync();
var accesstoken = token.AccessToken;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accesstoken);
var requestURl = new Uri($"https://xxxx.azurewebsites.net/weatherforecast");
var response = client.GetAsync(requestURl).Result;
string result = response.Content.ReadAsStringAsync().Result;
}
I'm trying out OpenIddict 3.0 for use in a SSO app. I followed the steps in the documentation, created an Authorize controller, and added a test application. When I try to connect to authorize I get this exception:
System.InvalidOperationException: The authorization request was not handled. To handle authorization requests, create a class implementing 'IOpenIddictServerHandler' and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.
Alternatively, enable the pass-through mode to handle them at a later stage.
I can't find anything in the documentation or sample apps that explains what this means. What am I missing?
Here's my code so far. In Startup.cs:
services.AddOpenIddict()
.AddCore(o =>
{
o.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
})
.AddServer(o =>
{
o.SetTokenEndpointUris("/connect/token");
o.SetAuthorizationEndpointUris("/connect/authorize");
o.AllowAuthorizationCodeFlow();
o.RegisterScopes(OpenIddictConstants.Scopes.Email);
o.AcceptAnonymousClients();
o.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
o.UseAspNetCore()
.EnableTokenEndpointPassthrough()
.DisableTransportSecurityRequirement();
})
.AddValidation(o =>
{
o.UseLocalServer();
o.UseAspNetCore();
});
And test app description:
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "test-app",
DisplayName = "Test Application",
PostLogoutRedirectUris = { new Uri("https://oidcdebugger.com/debug") },
RedirectUris = { new Uri("https://oidcdebugger.com/debug") }
};
I'm testing with the OpenID Connect debugger.
To handle authorization requests in a MVC controller, you must tell OpenIddict's ASP.NET Core host to use the pass-through mode, exactly like what you did for the token endpoint:
services.AddOpenIddict()
.AddServer(options =>
{
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough() // Add this line.
.EnableTokenEndpointPassthrough()
.DisableTransportSecurityRequirement();
});
I'm trying to setup Swagger in my AspNetCore 2.1 application using Azure Active Directory V2 but I cannot seem to get it right. I am able to configure the setup so that swagger prompts, redirects and successfully authenticates my client/user but when passing the bearer token to the server results in the error Bearer error="invalid_token", error_description="The signature is invalid". I have created a GitHub repository with the project I am trying to get work with all its configuration (https://github.com/alucard112/auth-problem)
I have managed to get the V1 endpoint working, by setting the resource to the Client Id of the AAD app, which results in the JWT token having the 'aud' set to the app client Id. In the V2 endpoint the 'aud' is being set to what I think is the Graph API resource '00000003-0000-0000-c000-000000000000'. I believe this is my problem at the moment, although am not 100 % sure. The V2 endpoints don't seem to have a way to define the audience like the V1 did unless of course there is some oversight from my side.
My Startup file is structured as follows:
The authentication is setup as the following:
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority = $"https://login.microsoftonline.com/{tenantId}";
options.TokenValidationParameters = new TokenValidationParameters
{
// In multi-tenant apps you should disable issuer validation:
ValidateIssuer = false,
// In case you want to allow only specific tenants,
// you can set the ValidIssuers property to a list of valid issuer ids
// or specify a delegate for the IssuerValidator property, e.g.
// IssuerValidator = (issuer, token, parameters) => {}
// the validator should return the issuer string
// if it is valid and throw an exception if not
};
});
And the swagger is setup as follows:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Title = "Protected Api",
});
c.OperationFilter<SecurityRequirementsOperationFilter>();
//IMATE - StevensW
// Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow)
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize",
TokenUrl = $"https://login.microsoftonline.com/common/{tenantId}/v2.0/token",
Scopes = new Dictionary<string, string>
{
{ "openid", "Unsure" },
{ "profile", "Also Unsure" }
}
});
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.OAuthClientId(Configuration.GetValue<string>("AzureAd:ClientId"));
c.OAuthAppName("Protected API");
// c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
// NEVER set the client secret here. It will ve exposed in the html of the swagger page if you "view source" and its not needed for OpenID Auth
// c.OAuthClientSecret(Configuration.GetValue<string>("AzureAd:ClientId"));
});
I am hoping to configure the swagger UI to use AAD's V2 endpoint and allow for a multi-tenant login that allows successfully authenticated API calls to be executed. Any help or direction would be greatly appreciated.
I ended up fixing the problem I was having. Working through this post helped me understand my mistakes.
The first mistake was my actual AAD app registration. I had not set a scope for the application under "Expose an API". Because they deprecated the resource property in V2, the way you would set the resource was to create a scope with the format api"//{application ID}/{scope_name}. After I made this change my AAD application was now correctly configured.
After that, I needed to add an additional section to my startup file:
return services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
// This is an Azure AD v2.0 Web API
options.Authority += "/v2.0";
// The valid audiences are both the Client ID (options.Audience) and api://{ClientID}
options.TokenValidationParameters.ValidAudiences = new string[] { options.Audience, $"api://{options.Audience}" };
options.TokenValidationParameters.ValidateIssuer = false;
});
Note: the link above provided an alternative solution to turning off the validation of the issuer if anyone is interested.
My AppSettings file was also simplified by only needing to define the Instance, TenantId, and ClientId.
Then from a swagger perspective, I just needed to add an additional scope to the security definition matching the one I created in my AAD application.
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
TokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token",
Scopes = new Dictionary<string, string>
{
{ "openid", "Sign In Permissions" },
{ "profile", "User Profile Permissions" },
{ $"api://{clientId}/access_as_user", "Application API Permissions" }
}
});
After these changes my application is now working as expected.
for v2 endpoint, update the accessTokenAcceptedVersion in Manifest of AAD from null to 2. It will work.