I added Token based authentication to my OWIN middleware and can generate the token. But while using, the token for an API call with Authorize attribute I always get "Authorization has been denied for this request." It works fine though without Authorize attribute. Here is my startup.cs and controller method. Any thoughts , what is wrong?
startup.cs
public void Configuration(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["issuer"];
var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthProvider(),
AccessTokenFormat = new JwtFormat(issuer)
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "*" },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
container = BuildDI();
var config = new HttpConfiguration();
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.MapHttpAttributeRoutes();
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ExternalBearer));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseCors(CorsOptions.AllowAll);
app.UseSerilogRequestContext("RequestId");
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
RegisterShutdownCallback(app, container);
}
public class SimpleAuthProvider: OAuthAuthorizationServerProvider
{
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
context.Rejected();
return Task.FromResult<object>(null);
}
var ticket = new AuthenticationTicket(SetClaimsIdentity(context), new AuthenticationProperties());
context.Validated(ticket);
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ExternalBearer);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
return identity;
}
}
API Controller Method:
[HttpGet]
[Route("sampleroute")]
[Authorize]
public async Task<HttpResponseMessage> GetSamples(string search)
{
try
{
HttpResponseMessage response;
using (HttpClient client = new HttpClient(Common.CreateHttpClientHandler()))
{
response = await client.GetAsync("test url");
}
var result = response.Content.ReadAsStringAsync().Result;
Samples[] sampleArray = JsonConvert.DeserializeObject<Samples[]>(result);
var filteredSamples = sampleArray .ToList().Where(y => y.NY_SampleName.ToUpper().Contains(search.ToUpper())).Select(n=>n);
log.Information("<==========Ended==========>");
return Request.CreateResponse(HttpStatusCode.OK,filteredSamples);
}
catch (Exception ex)
{
log.Error($"Error occured while pulling the Samples: {ex.ToString()}");
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.ToString());
}
}
It's probably a problem with the allowed audiences.
Here
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
...
AllowedAudiences = new[] { "*" },
...
}
you set the allowed audiences. The tokens audclaim will be checked against the list of AllowedAudiences. But you never add any audience to the token.
In our project I used a CustomJwtFormat based on the code shown in http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The token will be generated with a call to
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
the second parameter is responsible for the aud claim in the JWT:
From https://msdn.microsoft.com/en-us/library/dn451037(v=vs.114).aspx :
audience
Type: System.String
If this value is not null, a { aud, 'audience' } claim will be added.
After setting the aud claim in the token authorization should work fine.
From what I understood, you need to add the header: Authorization: Bearer "token".
If you have not modified the default implementation of the authorization request the steps are these:
Register user at the endpoint:
/api/Account/Register
Post to /token the following items:
grant_type: password
username: "the username you registered"
password: "the password you registered for the user"
You will receive a token in the Response
Copy that token and create a Request to the method you secured with the [Authorize] filter of type:
Authorization: Bearer "the_token_you_copied_earlier"
Needless to say, it could be pretty easy for you if you used Postman or Fiddler to make and receive Requests because it shows you how everything works.
Related
I'm building .NET 5 MVC Project and I am trying to implement JWT based Authentication.
I am able to get the user Username and Password, validate if it exist in the database and create a valid JWT token string, but after this I don't know how to actually save the token into the browser, so that I can authenticate and authorize later on.
I am unable to access controllers that use the [Authorize] attribute.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//Skipping code for clearence
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//Skipping code for clearence
app.UseAuthentication();
app.UseAuthorization();
}
appsettings.json
{
"Jwt": {
"Key": "SomeKey",
"Issuer": "SomeIssuer",
"Audience": "SomeIssuer"
}
}
CreateToken method in one of my classes, which it works and generates valid JWT Token string
public string CreateToken(User user, string roleName)
{
const int EXPIRY_DURATION_MINUTES = 120;
var claims = new[] {
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, roleName),
new Claim(ClaimTypes.NameIdentifier,
Guid.NewGuid().ToString())
};
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var tokenDescriptor = new JwtSecurityToken(_configuration["jwt:Issuer"], _configuration["Jwt:Audience"], claims,
expires: DateTime.Now.AddMinutes(EXPIRY_DURATION_MINUTES), signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);
}
SignIn action in my Account controller. What I am trying to achieve is to store the token in the browser and use the default authentication and role based authorization behaviour of .NET 5
[HttpPost]
public async Task<IActionResult> SignIn(SignInUserRequestModel request)
{
try
{
var token = await _authenticationService.SignInUser(request);
HttpContext.Response.Headers.Add("Authorization", $"Bearer {token}");
return Ok(token);
}
catch (ArgumentException exception)
{
return BadRequest(new { Code = 400, Message = exception.Message });
}
}
I'm working on an ASP.Net Core API project.
There are 2 controllers, controller A and B, I use Postman to pass my username and password to controller A action method, and it successfully returns a JWT.
After I pass the JWT to controller B, I hope to get the username and role information of the credential which I had input into the controller A.
The code in controller B below shows the way I get the user info.
public async Task<ActionResult> Get()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
var role = await _userManager.GetRolesAsync(user);
return Ok(user);
}
But after I sent the JWT, I got null user.
Edit1:
ConfigureServices method in my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "issuer",
ValidAudience = "audience",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("This is my super long security Key 123"))
};
});
services.AddDbContext<JWT_AuthContext>(option => option.UseSqlServer(Configuration.GetConnectionString("JWT_AuthContextConnection")));
}
IdentityHostingStartup.cs:
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext<JWT_AuthContext>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("JWT_AuthContextConnection")));
services.AddDefaultIdentity<JWT_AuthUser>()
.AddRoles<JWT_AuthRole>()
.AddEntityFrameworkStores<JWT_AuthContext>();
});
}
}
Controller Action for JWT generation (Controller A in the previous context):
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private UserManager<JWT_AuthUser> _userManager;
public AuthController(UserManager<JWT_AuthUser> userManager)
{
_userManager = userManager;
}
[HttpPost("token")]
public async Task<ActionResult> GetToken()
{
var header = Request.Headers["Authorization"];
if(header.ToString().StartsWith("Basic"))
{
var loginInfo = header.ToString().Substring("Basic ".Length).Trim();
var userNameAndPassword = Encoding.UTF8.GetString(Convert.FromBase64String(loginInfo)); //username:password
var userName = userNameAndPassword.Split(":")[0];
var password = userNameAndPassword.Split(":")[1];
var user = await _userManager.FindByNameAsync(userName);
if (user != null && await _userManager.CheckPasswordAsync(user,password))
{
//security key
string securityKey = "This is my super long security Key 123";
//symmetric security key
var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
//signing credentials
var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature);
//add claims
var claims = new List<Claim>();
//create token
var token = new JwtSecurityToken(
issuer: "issuer",
audience: "audience",
expires: DateTime.Now.AddHours(1),
claims: claims,
signingCredentials: signingCredentials
);
//return token
return Ok(new JwtSecurityTokenHandler().WriteToken(token));
}
}
return Unauthorized();
}
Controller method for getting user info (Controller B in the previous context):
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private UserManager<JWT_AuthUser> _userManager;
public ValuesController(UserManager<JWT_AuthUser> userManager)
{
_userManager = userManager;
}
// GET api/values
[HttpGet]
public async Task<ActionResult> Get()
{
var user = await _userManager.GetUserAsync(HttpContext.User);
var role = await _userManager.GetRolesAsync(user);
return Ok(user);
}
}
Edit 2:
Startup.cs Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
The reason is that you're using UserManager<JWT_AuthUser>.GetUserAsync(user) to get the principal, which belongs to the Identity Authentication Scheme. As the document describes, this method will only read the IdentityOptions.ClaimsIdentity.UserIdClaimType claim:
Returns the user corresponding to the IdentityOptions.ClaimsIdentity.UserIdClaimType claim in the principal or null.
Note the claims issued by your GetToken() method is an empty list:
[HttpPost("token")]
public async Task<ActionResult> GetToken()
{
....
//add claims
var claims = new List<Claim>();
//create token
var token = new JwtSecurityToken(
issuer: "issuer",
audience: "audience",
expires: DateTime.Now.AddHours(1),
claims: claims,
signingCredentials: signingCredentials
);
....
}
Since there's no such a IdentityOptions.ClaimsIdentity.UserIdClaimType claim, you'll get a null when invoking var user = await _userManager.GetUserAsync(HttpContext.User);.
And because the user is null, it throws when invoking await _userManager.GetRolesAsync(user);
How to Fix
Inject the IdentityOptions to get the UserIdClaimType:
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private UserManager<IdentityUser> _userManager;
private readonly IdentityOptions _idOpts;
public AuthController(UserManager<IdentityUser> userManager,IOptions<IdentityOptions> optionsAccessor)
{
_userManager = userManager;
this._idOpts = optionsAccessor.Value?? new IdentityOptions();
}
Add claims before sending the token:
//add claims
var claims = new List<Claim>();
claims.Add(new Claim(this._idOpts.ClaimsIdentity.UserIdClaimType, user.Id ));
//create token
var token = new JwtSecurityToken(
issuer: "issuer",
audience: "audience",
expires: DateTime.Now.AddHours(1),
claims: claims,
signingCredentials: signingCredentials
);
Another question inspired me enter link description here by the idea of holding user info inside JWT claim.
In the JWT generating action method inside Controller A, I use Usermanager to get user information of my credentials and put them into JWT claims.
var roles = await _userManager.GetRolesAsync(user);
var claims = new List<Claim>();
claims.Add( new Claim("Role", roles[0]));
After passing the JWT to controller B, this piece of code help me get the claim info:
var roleClaim = User.Claims.FirstOrDefault(x => x.Type.Equals("Role", StringComparison.InvariantCultureIgnoreCase));
Which fetch me the role
I created pone webapi and implemented authentication. I have the token method to get the user token. All working fine.
Scenario:
I tested the token method with the postman. Here I noted that I can use any type of HTTP method to request for the token. I think /token method should support POST method only. But when I use DELETE method also I got token. Same as, I can also use PUT, PATH etc.
Is this expected? I assume that it should return Method Not Supported other than POST requests.
You can write your custom OAuthAuthorizationServerOptions.Provider. And use the context to accept Http Post request only
OAuthAuthorizationServerOptions is asp.net identity core class. which you can find under this namespace Microsoft.Owin.Security.OAuth.
Sample Code:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.Request.Method != "POST")
{
context.SetError("invalid_request", "Invalid request");
return;
}
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
I'm using in my project asp.net web api 2, token authorize and when send request to /Token,I receive response
{
access_token: "u3XOCYV91f2P6odbceNIY_BnkfSpN7gQwzknsRi_.......0iRPlHYNMEES9",
token_type: "bearer",
expires_in: 1209599,
}
There is no argument UserName =( and when i send the request to the server with token on Authorize ActionResult,everything works well, but User.Identity.Name is null, User.Identity.isAuthenticated = true. Help pls.
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthorizeRepository _repo = new AuthorizeRepository())
{
User user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
For User.Identity.Name to be automatically populated, you must add a ClaimTypes.Name claim in your access token:
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
For my thesis Project i have to implement a token-based (Bearer) Authentication in my ASP.NET solution. I implemented it like Taiseer Jouseh (http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity).
The basic part is working correctly. I have a Mobile Client on which i can register a new User. Then i can Login and receive the token. When i then make a request, the token is sent in the Request Header. It all works fine.
My problem is, that I get an 401 Unauthorized error if I call a [Authorize] Method, even if i send the token. So i removed the [Authorize] Annotation to test some things:
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
Here i got alwas the same Identity: AuthenticationType=null; IsAuthenticated=false; Name=null; Claims:empty
var token = Request.Headers.Authorization;
Here i get the right token. So the token is sent by the request.
I hope you can help me. I have the token but no identity.
Here are parts of my code:
OAuthServiceProvider:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public async override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
// POST /token
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var userManager = DependencyResolver.Current.GetService<UserManager<IdentityUser, int>>();
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
The Controller Method:
#region GET /user/:id
[HttpGet]
[Route("{id:int:min(1)}")]
[ResponseType(typeof(UserEditDto))]
public async Task<IHttpActionResult> GetUser(int id)
{
try
{
// tests
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
var token = Request.Headers.Authorization;
User user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return NotFound();
}
Mapper.CreateMap<User, UserEditDto>();
return Ok(Mapper.Map<UserEditDto>(user));
}
catch (Exception exception)
{
throw;
}
}
#endregion
The WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
config.MapHttpAttributeRoutes();
var corsAttr = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttr);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Startup:
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
app.UseWebApi(config);
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
Finally I found the problem. It is so simple, that I can't believe I spent more than a week to solve this problem.
The problem was in the startup. I simply had to call ConfigureOAuth(app); before app.UseWebApi(config);
So the correct Startup looks like
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
My guess is that the token is not being sent correctly from the client. Are you sending the Authorization token with word "Bearer" prefixed in front ?
the Authorization Header should be something like below -
Authorization:Bearer reNjoIUZBJHCMigQJHzCgMMVUyu4vg
Look in the chrome developer tools network tab to see what is being sent.