WebTestClient mutateWith apparently not mutating - csrf

My (Cucumber) BDD unit test using WebTestClient is failing (with a 403 Forbidden), when I believe it should be passing. After some debugging, I established that this is because the CSRF check is failing, which suggests the mutateWith(csrf()) operation is not working. What am I doing wrong?
My test scenario:
Scenario Outline: Login
Given that player "<player>" exists with password "<password>"
And presenting a valid CSRF token
When log in as "<player>" using password "<password>"
Then program accepts the login
My test steps code (note the presence of client.mutateWith(csrf())):
#SpringBootTest(...)
#AutoConfigureWebTestClient
public class WebSteps {
#Autowired
private WebTestClient client;
...
private WebTestClient.ResponseSpec response;
#Given("presenting a valid CSRF token")
public void presenting_a_valid_CSRF_token() {
client.mutateWith(csrf());
}
#When("log in as {string} using password {string}")
public void log_in_as_using_password(final String player,
final String password) {
response = client.post().uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData("username", player)
.with("password", password))
.exchange();
}
#Then("program accepts the login")
public void program_accepts_the_login() {
response.expectStatus().isFound().expectHeader().valueEquals("Location",
"/");
}
...

Despite its name, the mutateWith() method does not really mutate its object. Rather, it returns a new object that has had the mutation applied. Therefore instead of writing
#Given("presenting a valid CSRF token")
public void presenting_a_valid_CSRF_token() {
client.mutateWith(csrf());
}
write
#Given("presenting a valid CSRF token")
public void presenting_a_valid_CSRF_token() {
client = client.mutateWith(csrf());
}
This error is more likely to occur in a Cucumber test because of the way that test steps alter shared state (The client object), rather than use a fluent API with a long chain of calls.

Related

Can I force a logout or expiration of a JWT token?

For authentication currently we are using JWT, so once a token is created it's created for a lifetime, and if we set a time expire, the token will expire.
Is there any way to expire token?
While clicking log out button, I need to destroy the token.
I'm using ASP.NET Core WebAPI.
I think cancelling JWT is the best way to handle logout. Piotr explained well in his blog: Cancel JWT tokens
We will start with the interface:
public interface ITokenManager
{
Task<bool> IsCurrentActiveToken();
Task DeactivateCurrentAsync();
Task<bool> IsActiveAsync(string token);
Task DeactivateAsync(string token);
}
And process with its implementation, where the basic idea is to keep
track of deactivated tokens only and remove them from a cache when not
needed anymore (meaning when the expiry time passed) – they will be no
longer valid anyway.
public class TokenManager : ITokenManager
{
private readonly IDistributedCache _cache;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOptions<JwtOptions> _jwtOptions;
public TokenManager(IDistributedCache cache,
IHttpContextAccessor httpContextAccessor,
IOptions<JwtOptions> jwtOptions
)
{
_cache = cache;
_httpContextAccessor = httpContextAccessor;
_jwtOptions = jwtOptions;
}
public async Task<bool> IsCurrentActiveToken()
=> await IsActiveAsync(GetCurrentAsync());
public async Task DeactivateCurrentAsync()
=> await DeactivateAsync(GetCurrentAsync());
public async Task<bool> IsActiveAsync(string token)
=> await _cache.GetStringAsync(GetKey(token)) == null;
public async Task DeactivateAsync(string token)
=> await _cache.SetStringAsync(GetKey(token),
" ", new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(_jwtOptions.Value.ExpiryMinutes)
});
private string GetCurrentAsync()
{
var authorizationHeader = _httpContextAccessor
.HttpContext.Request.Headers["authorization"];
return authorizationHeader == StringValues.Empty
? string.Empty
: authorizationHeader.Single().Split(" ").Last();
}
private static string GetKey(string token)
=> $"tokens:{token}:deactivated";
}
As you can see, there are 2 helper methods that will use the current
HttpContext in order to make things even easier.
Next, let’s create a middleware that will check if the token was
deactivated or not. That’s the reason why we should keep them in cache
– hitting the database with every request instead would probably kill
your app sooner or later (or at least make it really, really slow):
public class TokenManagerMiddleware : IMiddleware
{
private readonly ITokenManager _tokenManager;
public TokenManagerMiddleware(ITokenManager tokenManager)
{
_tokenManager = tokenManager;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (await _tokenManager.IsCurrentActiveToken())
{
await next(context);
return;
}
context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
}
}
Eventually, let’s finish our journey with implementing an endpoint for
canceling the tokens:
[HttpPost("tokens/cancel")]
public async Task<IActionResult> CancelAccessToken()
{
await _tokenManager.DeactivateCurrentAsync();
return NoContent();
}
For sure, we could make it more sophisticated, via passing the token
via URL, or by canceling all of the existing user tokens at once
(which would require an additional implementation to keep track of
them), yet this is a basic sample that just works.
Make sure that you will register the required dependencies in your
container and configure the middleware:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<TokenManagerMiddleware>();
services.AddTransient<ITokenManager, Services.TokenManager>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddDistributedRedisCache(r => { r.Configuration = Configuration["redis:connectionString"];
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
...
app.UseAuthentication();
app.UseMiddleware<TokenManagerMiddleware>();
app.UseMvc();
}
And provide a configuration for Redis in appsettings.json file:
"redis": {
"connectionString": "localhost"
}
Try to run the application now and invoke the token cancellation[sic]
endpoint – that’s it.
Actually the best way to logout is just remove token from the client. And you can make lifetime of tokens short (5-15 minutes) and implement refresh tokens for additions security. In this case there are less chance for attacker to do something with your JWT
If you have implemented the login scenario with the refresh token, You can remove the refresh token from the server and then , and then you should remove the token from the client.

Micronaut: Test POST request

In my Micronaut app I have a simple REST controller:
public class Response {
private String code;
public Response(String code) {
this.code = code;
}
}
#Controller("/api/test")
public class TestController {
#Post("/")
public Response index() {
return new Response("OK");
}
}
How can I tests this edpoint? I tried using
#MicronautTest
public class TestControllerTest {
#Inject
EmbeddedServer server;
#Inject
#Client("/")
HttpClient client;
#Test
void testResponse() {
String response = client.toBlocking()
.retrieve(HttpRequest.POST("/api/test/")); // FIXME `HttpRequest.POST` requires body
assertEquals("{\"code\": \"OK\"}", response);
}
but HttpRequest.POST requires an additional body argument to be specified. In my case there is no body to be sent. (In the real code it is a request to initialize a new object and thus it has to be POST).
Usually, when you implement a POST action, you expect that there is a body sent with the request. In your example, you don't accept any POST body, but you still need to pass anything in the unit test.
You can instantiate the HttpRequest object in the following way:
HttpRequest.POST("/api/test/", "");
You can't pass null, it has to be some non-null value (like an empty string.)

How to forward jwt token in Vert.x REST service

I have a Vert.x REST service that receive requests with jwt tokens, and I want to call my another REST service passing received token. Between router handler and WebClient call I have a business logic layer. My question is if there is a method to provide token to webClient other than passing it explicitly through my business logic layer? In other words is it possible to retrieve somehow my RoutingContext and token from e.g. vertxContext or an other component?
Example code demonstrating what I would like to achieve:
Verticle cass
public class RestApiVerticle extends AbstractVerticle {
businessLogicService service;
#Override
public void start() throws Exception {
initService();
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
JWTAuth authProvider = JWTAuth.create(vertx, getAuthConfig());
router.route("/*").handler(JWTAuthHandler.create(authProvider));
router.route("/somePath").handler(this::handleRequest);
server.requestHandler(router::accept).listen(config().getInteger("port"));
}
private void handleRequest(RoutingContext context){
service.doSomeBusinessLogic(); //I could pass context here, but I thing this is not a proper way to do it, as business logic should not know about RequestContext
}
private void initService(){
ExternalAPICaller caller = new ExternalAPICaller(WebClient.create(vertx));
service = new BusinessLogicService(caller);
}
private JsonObject getAuthConfig() {
return new JsonObject();
}
}
BusinessLogicService:
public class BusinessLogicService {
ExternalAPICaller caller;
public BusinessLogicService(ExternalAPICaller caller){
this.caller = caller;
}
public void doSomeBusinessLogic(){
caller.doSth();
}
}
ExternalAPICaller:
public class ExternalAPICaller {
WebClient client;
public ExternalAPICaller(WebClient client){
this.client = client;
}
public void doSth(){
String TOKEN = null; // I would like to retrive here my token from some vertx component
client.post("externalAPIpath")
.putHeader("Authorization", "Bearer" + TOKEN)
.send(ctx -> {
//(..)
});
}
}
My implementation is in JavaScript (Node.js/Express), but I used cookies to send the JWT to the client.
res.cookie("auth", token);
return res.redirect(`http://localhost:3000/socialauthredirect`);
When you call your do business logic method you could pass the request authorization header value as it contains your untouched jwt token. Then on your web client add a header with that value and of course named authorization and your token is forwarded to the next service.

Problems with making ebay REST api call

I've write following code:
include('httpful.phar');
include('config.php');
use \Httpful\Request;
class fullfil_pol_data{
public $name='1dzien';
public $marketplace='EBAY_PL';
public $category=array('name' => 'ALL_EXCLUDING_MOTORS_VEHICLES');
public $czas_wysylki=array('value'=> 1, 'unit'=>'DAY');
}
class aukcja{
public $ilosc;
public $cat_id;
public $format;
public $opis;
}
class token{
public $token;
private $duration;
private $starts;
public function __construct($t, $d){
$this->token=$t;
$this->duration=$d;
$this->starts=time();
}
public function check(){
if($this->duration+$this->starts >= time()-15*60) ibej::get_token();
}
}
class ibej{
private $token;
private $sandbox=true;
public function get_token()
{
if(this->sandbox) $url='https://api.sandbox.ebay.com/identity/v1/oauth2/token';
$req= Request::post($url)->addHeader('Content-Type', 'application/x-www-form-urlencoded')
->addHeader('Authorization', 'Basic '.PASSES)
->body("grant_type=client_credentials&redirect_uri=Marketing_Desig-Marketin-wystaw-blzyg&scope=https://api.ebay.com/oauth/api_scope")->send();
$this->token = new token($req->body->access_token, $req->body->expires_in);
var_dump($this->token);
}
/*$url='https://api.sandbox.ebay.com/sell/inventory/v1/offer';
//$req= Request::post($url)->addHeader('Authorization', 'Bearer '.$token)
// ->addHeader('X-EBAY-C-MARKETPLACE-ID', 'EBAY-PL')
*/
public function post_fullfilment_policy()
{
if(this->sandbox) $url='https://api.sandbox.ebay.com/sell/account/v1/fulfillment_policy';
$test= new fullfil_pol_data;
$req= Request::post($url)->addHeader('Authorization', 'Bearer '.$this->token->token)
->addHeader('Accept', 'application/json')
->addheader('Content-Type', 'application/json')
->addHeader('X-EBAY-C-MARKETPLACE-ID', 'EBAY-US')
->body(json_encode($test))->send();
var_dump($req);
}
}
$ibej_api = new ibej;
$ibej_api->get_token();
$ibej_api->post_fullfilment_policy();
When I try to call fullfilmentpolicy (http://developer.ebay.com/Devzone/rest/api-ref/account/fulfillment_policy__post.html) with ->post_fullfilment_policy() method I've this error in response:
"{"errors":[{"errorId":1100,"domain":"ACCESS","category":"REQUEST","message":"Access denied","longMessage":"Insufficient permissions to fulfill the request."}]}"
I don't know what is wrong, I reckon that If I have obtained token, it should give me permissions to do things with my account. Could anyone help me?
It looks like you have created an Application token. In order to perform API operations on eBay that involve a seller's account you have to create a User Token.This is explained in the documentation. Note that the process involved in getting a user token requires the user to log into their eBay account to grant permission to your application. The process is very different to obtaining an application token.

How does REST authentication work for client-side apps?

I'm trying to design my first public API, and I'm trying to learn how REST works with authentication, especially in the context of completely client-side apps using js-frameworks, e.g., angularJS.
Say you have a client which is a browser application (i.e., HTML, JS, CSS only) served as static files from something like nginx using a javascript framework to consume a REST service from, e.g. something that requires a secret access key that's used to create a signature for each request to the service, something like Amazon S3.
In terms of authentication in this scenario, where you don't have a server-side application, how would the secret access key be handled, i.e., how do you get it, where do you store it, etc.? It would seem like a horrible security situation to serve the key for each request (even if it only happens once to bootstrap the application).
And even if you do have a light server-side application--how do you securely inform the client (which still calls the authenticated 3rd party API itself) what the signature should be for every request it could possibly make? I'm very confused by how this is supposed to be designed from either end.
I've done a few AngularJS apps and the way that I've found is to use an HttpModule like this one:
using System;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
namespace YourSolution.WebApp.Modules
{
public class BasicAuthenticationHttpModule : IHttpModule
{
public BasicAuthenticationHttpModule()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnApplicationAuthenticateRequest;
context.EndRequest += OnApplicationEndRequest;
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
private static bool CheckPassword(
string username, string password)
{
return username == password;
}
private static void AuthenticateUser(string credentials)
{
try
{
var encoding = Encoding.GetEncoding(
"iso-8859-1");
credentials = encoding.GetString(
Convert.FromBase64String(credentials));
var separator = credentials.IndexOf(':');
var name = credentials.Substring(0, separator);
var password = credentials.Substring(separator + 1);
var validated = CheckPassword(name, password);
if (!validated) return;
var identity = new GenericIdentity(name);
SetPrincipal(new GenericPrincipal(identity, null));
}
catch (FormatException)
{
}
}
private static void OnApplicationAuthenticateRequest(
object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
if (authHeader == null) return;
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
if (authHeaderVal.Scheme.Equals(
"basic",
StringComparison.OrdinalIgnoreCase)
&& authHeaderVal.Parameter != null)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
private static void OnApplicationEndRequest(
object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
//response.Headers.Add(
// "WWW-Authenticate",
// string.Format("Basic realm=\"{0}\"", Realm));
}
}
public void Dispose()
{
}
}
}
The most important part is inside CheckPassword method, there is where you should validate the credentials.
Another point is this line response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm)); if you don't comment this line, the classic login requested form will show up, and if you do comment this line you have to catch the 401 error in your requests.
If you want to know about realm: What is the “realm” in basic authentication.
Plus, you will need to register the module in your web.config file:
<system.webServer>
<modules>
<add
name="BasicAuthenticationHttpModule"
type="YourSolution.WebApp.Modules.BasicAuthenticationHttpModule" />
</modules>
</system.webServer>
Then I've added these two methods to deal with the authentication token:
// u: username; p: password
CreateBasicAuthenticationToken = function (u, p) {
var t = u + ':' + p;
var hat = btoa(t);
window.sessionStorage.setItem('basicauthtoken', 'basic ' + hat);
};
DestroyBasicAuthenticationToken = function () {
window.sessionStorage.removeItem('basicauthtoken');
};
The btoa method: The btoa() method of window object is used to convert a given string to a encoded data (using base-64 encoding) string.. Taken from: http://www.w3resource.com/javascript/client-object-property-method/window-btoa.php.
And last I've added the authtoken to the request header using the beforeSend:
$.ajax({
type: 'GET',
url: 'your url',
beforeSend: function (xhr) {
window.sessionStorage.getItem('basicauthtoken');
}
}).done(function (data, textStatus, xhr) {
//...
});
Please do note using jQuery outside an angular directive is not recommended, AngularJS best practices dictates jQuery code must be always placed inside a directive.
Hope it helps.