This is a dotnet asp core 3 react application.
My startup.cs looks like this:
public class JwtAuthentication
{
public string SecurityKey { get; set; }
public string ValidIssuer { get; set; }
public string ValidAudience { get; set; }
public SymmetricSecurityKey SymmetricSecurityKey => new SymmetricSecurityKey(Convert.FromBase64String(SecurityKey));
public SigningCredentials SigningCredentials => new SigningCredentials(SymmetricSecurityKey, SecurityAlgorithms.HmacSha256);
}
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
private readonly IOptions<JwtAuthentication> _jwtAuthentication;
public ConfigureJwtBearerOptions(IOptions<JwtAuthentication> jwtAuthentication)
{
_jwtAuthentication = jwtAuthentication ?? throw new System.ArgumentNullException(nameof(jwtAuthentication));
}
public void PostConfigure(string name, JwtBearerOptions options)
{
var jwtAuthentication = _jwtAuthentication.Value;
options.ClaimsIssuer = jwtAuthentication.ValidIssuer;
options.IncludeErrorDetails = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateActor = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtAuthentication.ValidIssuer,
ValidAudience = jwtAuthentication.ValidAudience,
IssuerSigningKey = jwtAuthentication.SymmetricSecurityKey,
NameClaimType = ClaimTypes.NameIdentifier
};
}
}
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
var connectionStringOs =
"Server=xx.xx.xx.xxIntegrated Security=false;Trusted_Connection=false;Database=Options;User Id=xx;Password=xx;Connection Timeout=60";
var connectionStringDs =
"Server=xx.xx.xx.x;Integrated Security=false;Trusted_Connection=false;Database=DY;User Id=xx;Password=xx";
services.AddDbContext<OptionsDbContext>(o =>
o.UseSqlServer(connectionStringOs));
services.AddDbContext<DYDbContext>(o =>
o.UseSqlServer(connectionStringDs));
//services.AddRazorPages();
services.AddMvc();
services.AddMvc(option => option.EnableEndpointRouting = false);
services.Configure<JwtAuthentication>(Configuration.GetSection("JwtAuthentication"));
// I use PostConfigureOptions to be able to use dependency injection for the configuration
// For simple needs, you can set the configuration directly in AddJwtBearer()
services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>, ConfigureJwtBearerOptions>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
//public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
public void Configure(IApplicationBuilder app,
//IHostingEnvironment env,
IHostEnvironment env,
OptionsDbContext optionsDbContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
//{
// HotModuleReplacement = true
//});
}
app.UseStaticFiles();
/// Endpoint aware middleware.
// Middleware can use metadata from the matched endpoint.
//app.UseCookiePolicy();
app.UseAuthorization();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id:int?}");
});
}
}
I add a controller to get a token:
using System;
using System.Linq;
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using OptionsAPI.Entities;
[Route("user/[controller]")]
public class UserController : Controller
{
private readonly IOptions<JwtAuthentication> _jwtAuthentication;
public UserController(IOptions<JwtAuthentication> jwtAuthentication)
{
_jwtAuthentication = jwtAuthentication ?? throw new ArgumentNullException(nameof(jwtAuthentication));
}
[HttpPost]
[AllowAnonymous]
public IActionResult GenerateToken([FromBody]GenerateTokenModel model)
{
// TODO use your actual logic to validate a user
if (model.Password != "654321")
return BadRequest("Username or password is invalid");
var token = new JwtSecurityToken(
issuer: _jwtAuthentication.Value.ValidIssuer,
audience: _jwtAuthentication.Value.ValidAudience,
claims: new[]
{
// You can add more claims if you want
new Claim(JwtRegisteredClaimNames.Sub, model.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
},
expires: DateTime.UtcNow.AddDays(30),
notBefore: DateTime.UtcNow,
signingCredentials: _jwtAuthentication.Value.SigningCredentials);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
public class GenerateTokenModel
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}
I have an html file to test this:
<script type="text/javascript">
const response = await fetch("http://www.awebsite.com/user/generatetoken", {
method: "POST",
body: JSON.stringify({
username: "foo#bar",
password: "654321"
}),
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
});
const json = await response.json();
const token = json.token;
console.log(token);
</script>
When I load this into a browser, nothing happens. Not sure how to test calling the controller that hands a token to be used by an API, or if this code is missing something to tie it all together?
The first thing is to use Fiddler or browser's developer tools to trace the request and check the error message . But keep in mind that in order to call a function using the await keyword, it must be within the async function :
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
"Accept": "application/json"
},
body: JSON.stringify(data)
});
return await response.json();
}
postData('http://www.awebsite.com/user/generatetoken', {
username: "foo#bar",
password: "654321"
})
.then((data) => {
console.log(data.token);
});
Or using :
fetch('http://www.awebsite.com/user/generatetoken', {
method: "POST",
body: JSON.stringify({
username: "foo#bar",
password: "654321"
}),
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
})
.then(response => response.json()).then(data => {
alert(data.token)
});
Take care of other potential issues like CORS .
Related
i have a POST request using springboot, everything works fine when i make tests on postman but when i try it from the browser i get this error,
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.
this is my code.
the Service class
#Transactional(rollbackFor = {SQLException.class})
public ResponseEntity<Message> save(Cursos cursos) {
Optional<Cursos> optionalCursos = cursosRepository.findByTituloCursos(cursos.getTituloCursos());
if (optionalCursos.isPresent()) {
return new ResponseEntity<>(new Message("la entrada ya existe", null), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(new Message("ok", false, cursosRepository.saveAndFlush(cursos)), HttpStatus.OK);
}
DTO class
public class CursosDTO {
long id;
#NotNull
String tituloCursos;
#NotNull
String cuerpocursos;
public CursosDTO() {
}
public CursosDTO(long id, String tituloCursos, String cuerpocursos) {
this.id = id;
this.tituloCursos = tituloCursos;
this.cuerpocursos = cuerpocursos;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTituloCursos() {
return tituloCursos;
}
public void setTituloCursos(String tituloCursos) {
this.tituloCursos = tituloCursos;
}
public String getCuerpocursos() {
return cuerpocursos;
}
public void setCuerpocursos(String cuerpocursos) {
this.cuerpocursos = cuerpocursos;
}
}
controller class
#PostMapping("/")
public ResponseEntity<Message> save(#RequestBody CursosDTO cursosDTO) {
Cursos saveCursos = new Cursos(cursosDTO.getTituloCursos(), cursosDTO.getCuerpocursos());
return cursosService.save(saveCursos);
}
and this is my JavaScript code
fetch(url)
.then((response) => response.json())
.then((data) => {
console.log(data);
let dataUpd = {
tituloCursos: titulo,
cuerpocursos: contenido
};
console.log(JSON.stringify(dataUpd) + " prueba");
fetch(url, {
method: "POST",
BODY: dataUpd,
headers: {
"Content-Type": "application/json",
},
})
.then((res) => res.json())
.catch((error) => console.error("error al subir datos: ", error))
.then((response) => {
console.log("Datos subidos: ", response);
})
})
when i fetch data it brings all the data stored in the db correctly and this is the info that im trying to store
{"tituloCursos":"Stack","cuerpocursos":"<p>Overflown</p>"}
just in case it is relevant im using edge browser and im trying to store info from a rich text editor using tinymce
Probably a very simple typo. You capitalized BODY in your Post requests js code. It needs to be lowercase: body
See: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#supplying_request_options
Also, you would need to JSON.stringify your Data so your request options for the Post request would look like this:
method: "POST",
body: JSON.stringify(dataUpd),
headers: {
"Content-Type": "application/json",
},
I am trying to call an asp.net core 5 API from a react code but I keep getting the error 415 from the server.
Here is my server endpoint where the frontend is trying to call
public class OauthToken
{
public string TokenId;
}
[AllowAnonymous]
[HttpPost("signin-google")]
[Consumes("application/json")]
public async Task<IActionResult> GoogleLogin(OauthToken userView)
{
....
}
And the frontend code is as follows:
const googleResponse = (response) => {
const options = {
method: 'POST',
body: { TokenId: response.tokenId },
mode: 'no-cors',
accepts: "application/json",
cache: 'default',
contentType : "application/json",
}
fetch(config.GOOGLE_AUTH_CALLBACK_URL, options)
.then(r => {
console.log(r)
})
.catch(e=>{
console.log(e)
})
}
Below is my startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddCors(opts =>
{
opts.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
//.AllowCredentials();
});
});
services.AddAuthentication()
.AddIdentityServerJwt()
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AppSettings:JwtSecret"])),
ValidateIssuer = false,
ValidateAudience = false
};
})
.AddGoogle(opt =>
{
opt.ClientId = "MY_CLIENT_ID";
opt.ClientSecret = "MY_CLIENT_SECRET";
opt.SignInScheme = IdentityConstants.ExternalScheme;
})
//.AddTwitter(twitterOptions => { })
.AddFacebook(facebookOptions => {
facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
});
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// 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.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "/api/v1/{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
I don't know where the issue is coming from.
Please help me resolve this issue.
Thank you.
Based on your comment I have tested and reproduce your issue as you can have a look below:
Reproduced Issue:
How do I call the endpoint to get the data get to it?:
If you could have a look into your client-side code and HTTP Verb it should sent request within the FromBody but you are not sending that way, so you have two way to achieve that:
Way 1 : Set [FromBody] On Method:
[AllowAnonymous]
[HttpPost("signin-google")]
[Consumes("application/json")]
public async Task<IActionResult> GoogleLogin([FromBody] OauthToken userView)
{
return Ok();
}
Output:
Way 2 : Set TokenId as string on Method:
[AllowAnonymous]
[HttpPost("signin-google")]
[Consumes("application/json")]
public async Task<IActionResult> GoogleLogin(string TokenId)
{
return Ok();
}
Output:
Note: So you could try above steps to call your API endpoint accordingly. I noticed that problem was in API routing and method argurment additionally, I would suggest you to have a look on our
offical docs for indepth insight here
Hope it would guide you as expected and help to resolve the issue.
I finally got the code working after making the following changes:
I noticed that the TokenId property of OathToken class had no getter and no setter. So, I updated it as follows:
public class OauthToken
{
//added {get; set;}
public string TokenId { get; set; }
}
Changed the body of the fetch request from an object to a blob as follows:
const tokenBlob = new Blob([JSON.stringify({ TokenId: response.tokenId }, null, 2)], { type: 'application/json' });
Changed the mode of the request from "no-cors" to "cors" since Cors is already declared at the startup.cs class for the project
So, the updated working fetch request is as follows:
const googleResponse = (response) => {
const tokenBlob = new Blob([JSON.stringify({ TokenId: response.tokenId }, null, 2)], { type: 'application/json' });
const options = {
method: 'POST',
body: tokenBlob,
mode: 'cors',
accepts: "application/json",
cache: 'default',
contentType : "application/json",
}
fetch(config.GOOGLE_AUTH_CALLBACK_URL, options)
.then(r => {
r.json().then(user => {
console.log(user.tokenId);
});
})
.catch(e=>{
console.log(e)
})
}
And the code at the endpoint is as follows:
public class OauthToken
{
public string TokenId { get; set; }
}
[AllowAnonymous]
[Consumes("application/json")]
[HttpPost("signin-google")]
public async Task<IActionResult> GoogleLogin([FromBody] OauthToken clientToken)
{
return Ok(clientToken);
}
I guess this might help someone else facing such issues.
Thank you
i am currently learning Spring and i want to use it in my React Application.
I have created multiple Entities and now i want to create a new Entry within my React Application.
My User Entity looks like this:
#Entity
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
public User() {}
My Picklist Entity looks like this:
#Entity
#Table(name = "picklists")
#EntityListeners(AuditingEntityListener.class)
public class Picklists {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "createdbyid", referencedColumnName = "id")
private User createdById;
#Column(name = "label")
private String label;
public Picklists() {
}
My Picklist Controller:
#CrossOrigin
#RestController
#RequestMapping("/api/v1/")
public class PicklistsController {
private final PicklistRepository picklistRepository;
public PicklistsController(PicklistRepository picklistRepository) {
this.picklistRepository = picklistRepository;
}
#RequestMapping(value = "/createPicklistEntry", method = RequestMethod.POST)
public Picklists createObjectField(#RequestBody Picklists picklists) {
return this.picklistRepository.save(picklists);
}
}
I have tested to create a new Entity using Postman. This is my Request Body i'm sending:
{
"createdById": {
"userId": 1
},
"label": "Test"
}
But when sending the POST Request, i'm getting the following Error:
https://pastebin.com/ZbpjXF32
POST Method in React:
export const createItem = (url, item) => {
return new Promise((resolve, reject) => {
axios.post(url, item, {
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error)
});
})
}
useState:
const [selectedPicklist, setSelectedPicklist] = useState({
label: '',
createdById: {
userId: 1
}
});
Calling the POST Method:
createItem('http://localhost:8080/api/v1/createPicklistEntry', selectedPicklist)
.then(response => {
console.log(`Created Picklist: ${selectedPicklist}`);
})
.catch(error => {
console.log(error);
})
Add below attribute within #RequeatMapping annotation.
consumess = MediaType.APPLICATION_JSON_VALUE
Don't follow repository initialization like above and remove the constructor. Do the Autowire.
#Autowired
private final PicklistRepository picklistRepository;
I think your Controller can be simply.
#CrossOrigin
#RestController
#RequestMapping("/api/v1/")
public class PicklistsController {
private final PicklistRepository picklistRepository;
public PicklistsController(PicklistRepository picklistRepository) {
this.picklistRepository = picklistRepository;
}
#PostMapping(value = "/createPicklistEntry")
public Picklists createObjectField(#RequestBody Picklists picklists) {
return this.picklistRepository.save(picklists);
}
}
Then in your frontend I think it can be simply.
export const createItem = (url, item) => {
return new Promise((resolve, reject) => {
axios.post(url, item)
.then(response => {
resolve(response.data);
})
.catch(error => {
reject(error);
});
});
}
You should not need to pass any headers for JSON stuff. You can also use async/await rather than Promise if you prefer.
can some please point where the issue is.
the problem that i encounter is i have a controller that i added an attribute Authorize. So, when i try to access the actionResult GETDATA it says unable to find the action. but if remove the attribute Authorize, it's working as expected.
So everytime i make a request i add a jwt token on the header.
Here are codes:
**Angular 8 HttpInterceptor**
const currentUser = this.authenticationService.currentUserValue;
//if (currentUser && currentUser.authData) {
if (currentUser && currentUser.Token) {
debugger;
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.Token}`,
CurrentTabID: `${currentUser.CurrentTabID}`
}
});
}
**MyController**
[Authorize]
[ApiController]
[Route("[controller]")]
public class PatientController : ControllerBase
{
[HttpGet("GetTestData")]
//--These is the one i can't access
public IActionResult GetTestData()
{
return Ok("");
}
[AllowAnonymous]
[HttpGet("GetTestDataOne")]
public IActionResult GetTestDataOne()
{
return Ok("Hi John");
}
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=.; Database=blah;persist security info=True;user id=blah;password=blah;"
},
"AllowedHosts": "*",
"ApplicationSettings": {
"Secret": "1234567890123456",
"ClientURL": ""
}
}
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<PPMPBookingContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:Secret"].ToString());
// configure strongly typed settings objects
//var appSettingsSection = Configuration.GetSection("AppSettings");
//services.Configure<AppSettings>(appSettingsSection);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidateAudience = true,
ValidIssuer="vlad",
ValidAudience="Client"
};
});
// configure DI for application services
services.AddScoped<IUserService, UserService>();
services.AddScoped<IPracticeService, PracticeService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// 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.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
AccountController
public UserInfo Authenticate(int businessID, string username, string password)
{
// authentication successful so generate jwt token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_config.GetSection("ApplicationSettings:Secret").Value);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.ID.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
userInfo.Token = tokenHandler.WriteToken(token);
byte[] bytes = Encoding.GetEncoding(28591).GetBytes($"{businessID}{username}");
userInfo.AuthData = System.Convert.ToBase64String(bytes);
user.Password = null;
userInfo.User = user;
userInfo.BusinessID = businessID;
userInfo.Practice = _practiceService.PracticeInfo(businessID);
userInfo.CurrentTabID = Guid.NewGuid().ToString();
return userInfo;
}
I want to call the Token Endpoint of IdentityServer 4 from my React App (running on http://localhost:3000). So in some login method I am doing:
login = () => {
const userdata = {
username: 'admin',
password: 'admin',
};
const dataForBody = `${'client_id=js&'}${'grant_type=password&' +
'username='}${encodeURI(userdata.username)}&` +
`password=${encodeURI(userdata.password)}&` +
`scope=${encodeURI('api1')}`;
const messageHeaders = {
'Content-Type': 'application/x-www-form-urlencoded',
};
axios({
method: 'post',
url: 'http://localhost:5000/connect/token',
headers: messageHeaders,
data: dataForBody,
})
.then((response) => {
console.log(response);
});
}
Now I am getting the following response:
{"error":"unauthorized_client"}
My IdSrv set up is something like the js application sample.
config.cs
namespace QuickstartIdentityServer
{
public class Config
{
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
// client want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:3000/login" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
};
}
public static List<TestUser> GetUsers()
{
return new List<TestUser> {
new TestUser {
SubjectId = "1", Username = "admin", Password = "admin"
},
};
}
}
}
startup.cs
namespace QuickstartIdentityServer
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(LogLevel.Debug);
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
}
}
Am I missing something?
The problem is in the client definition:
AllowedGrantTypes = GrantTypes.Implicit,
is not correct. We have to use instead:
AllowedGrantTypes = ResourceOwnerPassword
The immediate problem that jumps out is that you are attempting to authenticate with the token service by passing the username and password as URL parameters. The client's username and password should be passed in using a standard basic authorization header:
Authorization: Basic Base64Encode(myusername:mypassword)
Which for this example would end up looking like this:
Authorization: Basic bXl1c2VybmFtZTpteXBhc3N3b3Jk