I am implementing IdentityServer4 an I am making 3 diferents proyects:
- IdentityServer (http://localhost:5000)
- API (http://localhost:5001)
- Javascript client (http://localhost:5003)
All the project are created with ASP.NET Core, but the JS Client use static files.
I need that the JS Client connect with the API only with identity token (not access token) because I only need to have access to the API, I dont need to manage user autentication.
I am reading the quickstarts post .html
As I read I consider that I need to user the Implicit Grand Type and I dont need OpenID Connect, only OAuth2.
Also I read this post .html But they use access token and I dont need that, to connect to the API I am using oidc-client-js library and I search the way to use with the Implicit Grand Type but the methods that I use redirect me to a http://localhost:5000/connect/authorize page (I think this is when I need to use OpenID Connect)
What is the best way to achieve that? What I have wrong? How can I autenticate with the api and call http://localhost:5001/values
IdentityServer Project
Config.cs
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "JavaScript Client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"http://localhost:5003/oidc-client-sample-callback.html"
},
AllowedCorsOrigins = new List<string>
{
"http://localhost:5003"
},
// scopes that client has access to
AllowedScopes = new List<string>
{
"api1"
}
}
};
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddDeveloperIdentityServer()
.AddInMemoryScopes(Config.GetScopes())
.AddInMemoryClients(Config.GetClients());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(LogLevel.Debug);
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
API project
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<ITodoRepository, TodoRepository>();
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:5003")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("default");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
ScopeName = "api1",
RequireHttpsMetadata = false
});
app.UseMvc();
}
ValuesController.cs
[Route("api/[controller]")]
[Authorize]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value3" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
Javascript client project
oidc-client-sample.html
<!DOCTYPE html>
<html>
<head>
<title>oidc-client test</title>
<link rel='stylesheet' href='app.css'>
</head>
<body>
<div>
<a href='/'>home</a>
<a href='oidc-client-sample.html'>clear url</a>
<label>
follow links
<input type="checkbox" id='links'>
</label>
<button id='signin'>signin</button>
<button id='processSignin'>process signin response</button>
<button id='signinDifferentCallback'>signin using different callback page</button>
<button id='signout'>signout</button>
<button id='processSignout'>process signout response</button>
</div>
<pre id='out'></pre>
<script src='oidc-client.js'></script>
<script src='log.js'></script>
<script src='oidc-client-sample.js'></script>
</body>
</html>
oidc-client-sample.js
///////////////////////////////
// UI event handlers
///////////////////////////////
document.getElementById('signin').addEventListener("click", signin, false);
document.getElementById('processSignin').addEventListener("click", processSigninResponse, false);
document.getElementById('signinDifferentCallback').addEventListener("click", signinDifferentCallback, false);
document.getElementById('signout').addEventListener("click", signout, false);
document.getElementById('processSignout').addEventListener("click", processSignoutResponse, false);
document.getElementById('links').addEventListener('change', toggleLinks, false);
///////////////////////////////
// OidcClient config
///////////////////////////////
Oidc.Log.logger = console;
Oidc.Log.level = Oidc.Log.INFO;
var settings = {
authority: 'http://localhost:5000/',
client_id: 'client',
redirect_uri: 'http://localhost:5003/oidc-client-sample-callback.html',
response_type: 'token',
scope: 'api1'
};
var client = new Oidc.OidcClient(settings);
///////////////////////////////
// functions for UI elements
///////////////////////////////
function signin() {
client.createSigninRequest({ data: { bar: 15 } }).then(function (req) {
log("signin request", req, "<a href='" + req.url + "'>go signin</a>");
if (followLinks()) {
window.location = req.url;
}
}).catch(function (err) {
log(err);
});
}
function api() {
client.getUser().then(function (user) {
var url = "http://localhost:5001/values";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
}
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
oidc-client-sample-callback.html
<!DOCTYPE html>
<html>
<head>
<title>oidc-client test</title>
<link rel='stylesheet' href='app.css'>
</head>
<body>
<div>
<a href="oidc-client-sample.html">back to sample</a>
</div>
<pre id='out'></pre>
<script src='log.js'></script>
<script src='oidc-client.js'></script>
<script>
Oidc.Log.logger = console;
Oidc.Log.logLevel = Oidc.Log.INFO;
new Oidc.OidcClient().processSigninResponse().then(function(response) {
log("signin response success", response);
}).catch(function(err) {
log(err);
});
</script>
</body>
</html>
I am implementing IdentityServer4 an I am making 3 diferents proyects:
- IdentityServer (http://localhost:5000)
- API (http://localhost:5001)
- Javascript client (http://localhost:5003)
All the project are created with ASP.NET Core, but the JS Client use static files.
I need that the JS Client connect with the API only with identity token (not access token) because I only need to have access to the API, I dont need to manage user autentication.
I am reading the quickstarts post https://identityserver4.readthedocs.io/en/dev/quickstarts/1_client_credentials.html
As I read I consider that I need to user the Implicit Grand Type and I dont need OpenID Connect, only OAuth2.
Also I read this post https://identityserver4.readthedocs.io/en/dev/quickstarts/7_javascript_client.html But they use access token and I dont need that, to connect to the API I am using oidc-client-js library https://github./IdentityModel/oidc-client-js and I search the way to use with the Implicit Grand Type but the methods that I use redirect me to a http://localhost:5000/connect/authorize page (I think this is when I need to use OpenID Connect)
What is the best way to achieve that? What I have wrong? How can I autenticate with the api and call http://localhost:5001/values
IdentityServer Project
Config.cs
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "JavaScript Client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"http://localhost:5003/oidc-client-sample-callback.html"
},
AllowedCorsOrigins = new List<string>
{
"http://localhost:5003"
},
// scopes that client has access to
AllowedScopes = new List<string>
{
"api1"
}
}
};
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddDeveloperIdentityServer()
.AddInMemoryScopes(Config.GetScopes())
.AddInMemoryClients(Config.GetClients());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(LogLevel.Debug);
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
}
API project
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<ITodoRepository, TodoRepository>();
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:5003")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("default");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
ScopeName = "api1",
RequireHttpsMetadata = false
});
app.UseMvc();
}
ValuesController.cs
[Route("api/[controller]")]
[Authorize]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value3" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
Javascript client project
oidc-client-sample.html
<!DOCTYPE html>
<html>
<head>
<title>oidc-client test</title>
<link rel='stylesheet' href='app.css'>
</head>
<body>
<div>
<a href='/'>home</a>
<a href='oidc-client-sample.html'>clear url</a>
<label>
follow links
<input type="checkbox" id='links'>
</label>
<button id='signin'>signin</button>
<button id='processSignin'>process signin response</button>
<button id='signinDifferentCallback'>signin using different callback page</button>
<button id='signout'>signout</button>
<button id='processSignout'>process signout response</button>
</div>
<pre id='out'></pre>
<script src='oidc-client.js'></script>
<script src='log.js'></script>
<script src='oidc-client-sample.js'></script>
</body>
</html>
oidc-client-sample.js
///////////////////////////////
// UI event handlers
///////////////////////////////
document.getElementById('signin').addEventListener("click", signin, false);
document.getElementById('processSignin').addEventListener("click", processSigninResponse, false);
document.getElementById('signinDifferentCallback').addEventListener("click", signinDifferentCallback, false);
document.getElementById('signout').addEventListener("click", signout, false);
document.getElementById('processSignout').addEventListener("click", processSignoutResponse, false);
document.getElementById('links').addEventListener('change', toggleLinks, false);
///////////////////////////////
// OidcClient config
///////////////////////////////
Oidc.Log.logger = console;
Oidc.Log.level = Oidc.Log.INFO;
var settings = {
authority: 'http://localhost:5000/',
client_id: 'client',
redirect_uri: 'http://localhost:5003/oidc-client-sample-callback.html',
response_type: 'token',
scope: 'api1'
};
var client = new Oidc.OidcClient(settings);
///////////////////////////////
// functions for UI elements
///////////////////////////////
function signin() {
client.createSigninRequest({ data: { bar: 15 } }).then(function (req) {
log("signin request", req, "<a href='" + req.url + "'>go signin</a>");
if (followLinks()) {
window.location = req.url;
}
}).catch(function (err) {
log(err);
});
}
function api() {
client.getUser().then(function (user) {
var url = "http://localhost:5001/values";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
}
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
oidc-client-sample-callback.html
<!DOCTYPE html>
<html>
<head>
<title>oidc-client test</title>
<link rel='stylesheet' href='app.css'>
</head>
<body>
<div>
<a href="oidc-client-sample.html">back to sample</a>
</div>
<pre id='out'></pre>
<script src='log.js'></script>
<script src='oidc-client.js'></script>
<script>
Oidc.Log.logger = console;
Oidc.Log.logLevel = Oidc.Log.INFO;
new Oidc.OidcClient().processSigninResponse().then(function(response) {
log("signin response success", response);
}).catch(function(err) {
log(err);
});
</script>
</body>
</html>
Share
Improve this question
edited Aug 16, 2017 at 7:55
Steven T. Cramer
1,5182 gold badges21 silver badges37 bronze badges
asked Sep 28, 2016 at 23:12
Sebastián ASebastián A
8802 gold badges7 silver badges24 bronze badges
4
- Why not make the API anonymous and save a bunch of work? You'll have to hardcode the Client ID + Client Secret in JavaScript, which means they're promised, there's no way to keep a secret in JavaScript. – stevieg Commented Sep 28, 2016 at 23:57
- Yes, I am reading more, and I need is the Implicit Grand Type aaronparecki./2012/07/29/2/oauth2-simplified but I dont know how to use it correctly – Sebastián A Commented Sep 29, 2016 at 0:21
- IdentityServer4 has a public api type this is what I am trying to use. – Steven T. Cramer Commented Aug 26, 2017 at 6:19
- I remend to read richard-banks/2018/11/… its a good start to see all thinks working – Mirusky Commented Apr 25, 2019 at 19:41
1 Answer
Reset to default 3As far as I see, your code should work, it does everything.
- Your JavaScript-app (localhost:5003) requests a token (
function signin()
). This will result in redirecting to IdentityServer - The IdentityServer (localhost:5000) is set up and the client settings (Client.cs) matches the request. Although configuration is missing for users and resources: see here: https://github./IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/3_ImplicitFlowAuthentication/src/QuickstartIdentityServer/Startup.cs
- Your JavaScript-app has a correct "landing page", a page where IdentityServer redirects back after successful login. This page picks up the newly issued token (
new Oidc.OidcClient().processSigninResponse()
) - Your JavaScript-app sends the token along its API-request (
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
) - Your API (localhost:5001) is set up correctly and will authorize against your IdentityServer
So I think the code is about right, but there're some misunderstandings regarding the terms.
- You need Implicit grant. Forget about ClientCredentials, because it is designed for another workflow and should not be used in a browser, because the client secret is being exposed. This means that anyone could issue a valid token ("steal") and your security is promised. (If you must use ClientCredentials, create a server proxy method).
- You need both OpenID Connect (OIDC) and OAuth2. (These are not accurate definitions!!) OIDC issues the token for you ("logs the user in"), while OAuth2 validates the token. Don't worry, IdentityServer takes care all of that.
- You need access token. Identity token is issued for the requestor (your localhost:5003 JavaScript application), Access token should be "sent forward" to an API (your localhost:5001 API)
- The redirect for "login" is normal. This is typical workflow for web application: you leave your application (localhost:5003) and ends up in IdentityServer (http://localhost:5000). After successful login, you're being redirected back to your application (localhost:5003)