I am using asp.netcore 3.1 and openapi 3.0.1
I have added authorization to my apis using the following code:
services.AddSwaggerGen(setupAction =>
{
setupAction.SwaggerDoc("APIs_Documentation", new OpenApiInfo
{
Title = "Project APIs",
Version = "1"
});
setupAction.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.Http,
Scheme = "bearer",
Description = "Enter token here",
Name = "Authorization",
#In = ParameterLocation.Header,
});
setupAction.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth2",
}
},new List<string>()}
});
});
Is there any way to set a default value to the value field of the popup authorization dialogue in the next image?
If you are looking for a way to add a default header you can do it from C# like this:
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.Interceptors = new InterceptorFunctions
{
RequestInterceptorFunction = "function (req) { req.headers['MyCustomHeader'] = 'CustomValue'; return req; }"
};
}
);
or want to add it to Swagger UI: related question
Related
I have an error that I don't understand.
I'm trying to validate the creation of a MangoPay account and I'm using the Http-Client package for the APIs (I've also installed mangopay's package).
When I try to create one, this error shows up:
Unsupported option "0" passed to "Symfony\Component\HttpClient\NativeHttpClient", did you mean "auth_basic", "auth_bearer", "query", "headers", "body", "json", "user_data", "max_redirects", "http_version", "base_uri", "buffer", "on_progress", "resolve", "proxy", "no_proxy", "timeout", "max_duration", "bindto", "verify_peer", "verify_host", "cafile", "capath", "local_cert", "local_pk", "passphrase", "ciphers", "peer_fingerprint", "capture_peer_cert_chain", "extra"?
That's the file I'm working on:
<?php
namespace App\Service;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use MangoPay;
use App\Service\MockStorage;
class CallApiService
{
private $mangoPayApi;
private $client;
public function __construct(HttpClientInterface $httpClient)
{
$this->client = $httpClient;
$this->mangoPayApi = new MangoPay\MangoPayApi();
$this->mangoPayApi->Config->ClientId = $_ENV['CLIENT_ID'];
$this->mangoPayApi->Config->ClientPassword = $_ENV['API_KEY'];
// $this->mangoPayApi->Config->TemporaryFolder = 'mangotemp';
$this->mangoPayApi->OAuthTokenManager->RegisterCustomStorageStrategy(new MockStorage());
}
public function createProfilMango($form)
{
$date = date_format($form['birthday']->getData(), "Ymd");
$userMango = $this->client->request(
'POST',
$_ENV['SERVER_URL'] . '/' . $_ENV['VERSION'] . '/' . $_ENV['CLIENT_ID'] .'/users/natural',
[
$UserNatural = new MangoPay\UserNatural(),
$UserNatural->FirstName = $form['firstname']->getData(),
$UserNatural->LastName = $form['lastname']->getData(),
$UserNatural->Email = $form['email']->getData(),
$UserNatural->Address = new MangoPay\Address(),
$UserNatural->Address->AddressLine1 = $form['streetNumber']->getData() . " " . $form['address']->getData(),
$UserNatural->Address->AddressLine2 = "",
$UserNatural->Address->City = $form['city']->getData(),
$UserNatural->Address->Region = "",
$UserNatural->Address->PostalCode = $form['zipCode']->getData(),
$UserNatural->Address->Country = "FR",
$UserNatural->Birthday = intval($date),
$UserNatural->Nationality = $form['nationality']->getData(),
$UserNatural->CountryOfResidence = "FR",
$UserNatural->Capacity = "NORMAL",
$Result = $this->mangoPayApi->Users->Create($UserNatural),
]
);
return $userMango;
}
}
The CallApiService.php is called upon the signup controller of my website:
// RegistrationController.php
[...]
public function register(CallApiService $callApiService, User $user = null, Request $request, UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, AppCustomAuthenticator $authenticator, EntityManagerInterface $entityManager): Response
{
if(!$user){
$user = new User();
}
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// encode the plain password
$user->setPassword(
$userPasswordHasher->hashPassword(
$user,
$form->get('plainPassword')->getData()
)
);
$callApiService->createProfilMango($form);
// $entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
// do anything else you need here, like send an email
// return $userAuthenticator->authenticateUser(
// $user,
// $authenticator,
// $request,
// // 'main' // firewall name in security.yaml
// );
}
return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
'editMode'=> $user-> getId() !== null,
]);
}
I've tried to change the $client value with new NativeHttpClient() and new CurlHttpClient() but the error doesn't change.
What's the error? How can I fix it?
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(); //this replaces these services.AddMvcCore().AddApiExplorer();
...
services.AddSwaggerGen(options =>
{
// make this automatic ???
options.SwaggerDoc("v1", new Info { Version = "v1", Title = "v1 API", Description = "v1 API Description" });
options.SwaggerDoc("v2", new Info { Version = "v2", Title = "v2 API", Description = "v2 API Description" });
...
options.DocInclusionPredicate((version, desc) =>
{
var versions = desc.CustomAttributes().OfType<ApiVersionAttribute>().SelectMany(attr => attr.Versions).ToArray();
var maps = desc.CustomAttributes().OfType<MapToApiVersionAttribute>().SelectMany(attr => attr.Versions).ToArray();
return versions.Any(v => $"v{v.ToString()}" == version) && (!maps.Any() || maps.Any(v => $"v{v.ToString()}" == version));
});
});
}
This code works as expected. But can the calls to SwaggerDoc be automated, in order to make the code more generic? In DocInclusionPredicate from the desc parameter the versions can be gathered.
As you are using the ApiVersionAttribute, I assume you are using the Microsoft.AspNetCore.Mvc.Versioning nuget package. The package provides a service named IApiVersionDescriptionProvider. This service provides an enumeration of all detected API-Versions. You can then automatically add them as a swagger-doc.
services.AddSwaggerGen(options =>
{
// you can use the IApiVersionDescriptionProvider
var provider = services.BuildServiceProvider()
.GetRequiredService<IApiVersionDescriptionProvider>();
foreach (var description in provider.ApiVersionDescriptions)
{
var info = new Info
{
Title = $"My API {description.ApiVersion}",
Version = description.ApiVersion.ToString(),
Contact = new Contact
{
Email = "info#mydomain.com",
Name = "Foo Bar",
Url = "https://thecatapi.com/"
}
};
options.SwaggerDoc(description.GroupName, info);
}
// instead of manually adding your versions
//options.SwaggerDoc("v1", new Info { Version = "v1", Title = "v1 API", Description = "v1 API Description" });
//options.SwaggerDoc("v2", new Info { Version = "v2", Title = "v2 API", Description = "v2 API Description" });
options.DocInclusionPredicate((version, desc) =>
{
var versions = desc.CustomAttributes().OfType<ApiVersionAttribute>().SelectMany(attr => attr.Versions).ToArray();
var maps = desc.CustomAttributes().OfType<MapToApiVersionAttribute>().SelectMany(attr => attr.Versions).ToArray();
return versions.Any(v => $"v{v.ToString()}" == version) && (!maps.Any() || maps.Any(v => $"v{v.ToString()}" == version));
});
});
I'm capturing model validation errors using the code below and outputting a custom 400 response from the CustomProblemDetails object which works great. My question is, I want to log from within the CustomProblemDetails object but don't see how I can use DI. I've passed in context which gives me access to the services but is this the way to go? If so it appears I can only get access to the ILoggerFactory how do I log using ILoggerFactory?
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});
For logging in InvalidModelStateResponseFactory, you could try code like:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Logger From Invalid Model");
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
logger.LogError(JsonConvert.SerializeObject(problemDetails));
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});
I am trying to setup a Web API with Swagger and an IdentityServer and can't figure out how to make Swagger works correctly.
My React app is working with the IdentityServer and I managed to get the ui working but when I try to activate authentication, I always get a "insufficient_scope" error.
Here's my config :
Client
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "ipassportimplicit",
ClientName = "iPassport (Implicit)",
Flow = Flows.Implicit,
AllowAccessToAllScopes = true,
//redirect = URI of the React application callback page
RedirectUris = new List<string>
{
Constants.iPassportReact + "callback.html"
}
},
new Client
{
ClientId = "swaggerui",
ClientName = "Swagger (Implicit)",
Flow = Flows.Implicit,
AllowAccessTokensViaBrowser = true,
PostLogoutRedirectUris = new List<string>
{
"http://localhost:53633/swagger/"
},
AllowAccessToAllScopes = true,
RedirectUris = new List<string>
{
"http://localhost:53633/swagger/ui/o2c-html"
}
}
};
}
Scope
public static IEnumerable<Scope> Get()
{
return new List<Scope>
{
new Scope
{
Name = "passportmanagement",
DisplayName = "Passport Management",
Description = "Allow the application to manage passports on your behalf.",
Type = ScopeType.Resource
},
new Scope
{
Name = "swagger",
DisplayName = "Swagger UI",
Description = "Display Swagger UI",
Type = ScopeType.Resource
}
};
}
SwaggerConfig
public static void Register(HttpConfiguration config)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
config
.EnableSwagger(c =>
{
c.SingleApiVersion("v2", "api_iPassport");
c.OAuth2("oauth2")
.Description("OAuth2 Implicit Grant")
.Flow("implicit")
.AuthorizationUrl(Constants.iPassportSTSAuthorizationEndpoint)
.TokenUrl(Constants.iPassportSTSTokenEndpoint)
.Scopes(scopes =>
{
scopes.Add("swagger", "Swagger UI");
});
c.OperationFilter<AssignOAuth2SecurityRequirements>();
})
.EnableSwaggerUi(c =>
{
c.EnableOAuth2Support("swaggerui", "swaggerrealm", "Swagger UI");
});
}
Operation Filter
public class AssignOAuth2SecurityRequirements : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var actFilters = apiDescription.ActionDescriptor.GetFilterPipeline();
var allowsAnonymous = actFilters.Select(f => f.Instance).OfType<OverrideAuthorizationAttribute>().Any();
if (allowsAnonymous)
return; // must be an anonymous method
//var scopes = apiDescription.ActionDescriptor.GetFilterPipeline()
// .Select(filterInfo => filterInfo.Instance)
// .OfType<AllowAnonymousAttribute>()
// .SelectMany(attr => attr.Roles.Split(','))
// .Distinct();
if (operation.security == null)
operation.security = new List<IDictionary<string, IEnumerable<string>>>();
var oAuthRequirements = new Dictionary<string, IEnumerable<string>>
{
{"oauth2", new List<string> {"swagger"}}
};
operation.security.Add(oAuthRequirements);
}
}
Response Headers
{
"date": "Fri, 12 May 2017 03:37:08 GMT",
"www-authenticate": "Bearer error=\"insufficient_scope\"",
"x-sourcefiles": "=?UTF-8?B?TzpcTG9jYWwgV29ya3NwYWNlXFZTVFMgSUJNXFJlcG9zXFdlYkFQSVxhcGlfaVBhc3Nwb3J0XGFwaV9pUGFzc3BvcnRcYXBpXFVzZXJcR2V0?=",
"server": "Microsoft-IIS/10.0",
"x-powered-by": "ASP.NET",
"content-length": "0",
"content-type": null
}
Anything I can't see? All help appreciated!
Thanks
My problem was in my Startup.cs class of the Web API in which I didn't add the required scope to the
public void ConfigureAuth(IAppBuilder app)
{
var options = new IdentityServerBearerTokenAuthenticationOptions()
{
Authority = Constants.iPassportSTS,
RequiredScopes = new[] { "passportmanagement", "swagger" }
};
app.UseIdentityServerBearerTokenAuthentication(options);
}
I am bound to the restrictions of my webservice: It expects a json-payload!
So, doing something like
var ajaxAppender = new log4javascript.AjaxAppender("clientLogger");
var jsonLayout = new log4javascript.JsonLayout();
ajaxAppender.setLayout(jsonLayout);
log.addAppender(ajaxAppender);
won't work, as it creates two keys in the forms-collection (data and layout).
How can I, with built-in options, get a json-payload?
I've created a JsonAppender
function JsonAppender(url) {
var isSupported = true;
var successCallback = function(data, textStatus, jqXHR) { return; };
if (!url) {
isSupported = false;
}
this.setSuccessCallback = function(successCallbackParam) {
successCallback = successCallbackParam;
};
this.append = function (loggingEvent) {
if (!isSupported) {
return;
}
$.post(url, {
'logger': loggingEvent.logger.name,
'timestamp': loggingEvent.timeStampInMilliseconds,
'level': loggingEvent.level.name,
'url': window.location.href,
'message': loggingEvent.getCombinedMessages(),
'exception': loggingEvent.getThrowableStrRep()
}, successCallback, 'json');
};
}
JsonAppender.prototype = new log4javascript.Appender();
JsonAppender.prototype.toString = function() {
return 'JsonAppender';
};
log4javascript.JsonAppender = JsonAppender;
used like so
var logger = log4javascript.getLogger('clientLogger');
var jsonAppender = new JsonAppender(url);
logger.addAppender(jsonAppender);
According to log4javascript's change log, with version 1.4.5, there is no longer the need to write a custom appender, if the details sent by Log4Javascript suffice.
1.4.5 (20/2/2013)
- Changed AjaxAppender to send raw data rather than URL-encoded form data when
content-type is not "application/x-www-form-urlencoded"
https://github.com/DECK36/log4javascript/blob/master/changelog.txt
Simply adding the 'Content-Type' header to the AjaxAppender and setting it to 'application/json' is enough
ajaxAppender.addHeader("Content-Type", "application/json;charset=utf-8");
A quick test using fiddler shows that log4javascipt sends a collection of objects. Here's a sample of the payload:
[{
"logger": "myLogger",
"timestamp": 1441881152618,
"level": "DEBUG",
"url": "http://localhost:5117/Test.Html",
"message": "Testing message"
}]