identity server 4 securing api on the same project - identityserver4

We are using IdentityServer as an openid provider for our web applications and APIs resources.
I want to expose a secure api endpoint on identity server for editing users, somehow I can not get configuration working. my client is angular and I have a valid bearer token.
app.UseCors("AllowSpecificOrigin");
app.UseIdentity();
app.UseIdentityServer();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = Configuration["AuthServerUrl"],
ScopeName = "api",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false
});
any help will be appreciated.

You can branch your application with using MapWhen like below:
app.MapWhen(x => x.Request.Path.StartsWithSegments("/custom"), builder =>
{
builder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = Configuration["AuthServerUrl"],
ScopeName = "api",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false
});
// .....
});
app.UseIdentity();
app.UseIdentityServer();
//...

Related

Getting CORS policy error using Identity Server and Angular application

I'm getting a CORS error when I try to access Web.API endpoint. I have an agular application, identity server for authentication and web.api for the data management.
The API runs on port :52177, Angular APP on :52178, and IS4 on :4165.
Here are the IS Configuration
new Client {
RequireConsent = false,
ClientId = "angular_spa",
ClientName = "Angular SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = { "openid", "profile" },
RedirectUris =
{
"http://localhost:52178/auth-callback"
},
PostLogoutRedirectUris = {"http://localhost:52178/?logout=true"},
AllowedCorsOrigins =
{
"http://localhost:52178",
"http://localhost:52177"
},
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 3600
}
Angular APP
return {
authority: 'http://localhost:4165',
client_id: 'angular_spa',
redirect_uri: 'http://localhost:52178/auth-callback',
post_logout_redirect_uri: 'http://localhost:52178/?logout=true',
response_type: "id_token token",
scope: "openid profile",
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true
};
API
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://localhost:4165",
RequiredScopes = new[] { "openid", "profile" },
PreserveAccessToken = true,
NameClaimType = System.Security.Claims.ClaimTypes.Name,
RoleClaimType = System.Security.Claims.ClaimTypes.Role
});
This is the error I'm getting
Access to XMLHttpRequest at 'http://localhost:52177/api/books?length=0&pageIndex=0&pageSize=10' from origin 'http://localhost:52178' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I added both :52178 and : 52177 Origins to the Client config for the IS4 but its still doesn't work. Any ideas what I'm missing?
As #mackie mentioned, The API itself will need to have CORS enabled. I had to install Install-Package Microsoft.AspNet.WebApi.Cors and at the WebApiConfig enable CORS for my client application
var cors = new EnableCorsAttribute("http://localhost:52178", "*", "*");
config.EnableCors(cors);

SilentRenewService._tokenExpiring: Error from signinSilent: Frame window timed out t.error

I am following the pluralsight course Securing Angular Apps with OpenID Connect and OAuth2 to get up and running with oidc-client in Angular, but I have come across an issue with the silent refresh token, which throws
SilentRenewService._tokenExpiring: Error from signinSilent: Frame window timed out t.error
on the server the client is
new Client
{
ClientId = "spa-client",
ClientName = "Projects SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RedirectUris = {
"http://localhost:4200/assets/oidc-login-redirect.html",
"http://localhost:4200/assets/silent-redirect.html"
},
PostLogoutRedirectUris = { "http://localhost:4200/?postLogout=true" },
AllowedCorsOrigins = { "http://localhost:4200/" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"projects-api"
},
IdentityTokenLifetime=30,
AccessTokenLifetime=30
}
and the config on the client is:
var config = {
authority: 'http://localhost:4242/',
client_id: 'spa-client',
redirect_uri: 'http://localhost:4200/assets/oidc-login-redirect.html',
scope: 'openid projects-api profile',
response_type: 'id_token token',
post_logout_redirect_uri: 'http://localhost:4200/?postLogout=true'
userStore: new WebStorageStateStore({ store: window.localStorage }),
automaticSilentRenew: true,
silent_redirect_uri: 'http://localhost:4200/assets/silent-redirect.html'
};
I found a suggested solution from "Sohan" for a similar problem here (this is specifically for azure AD). This then causes
Frame window timed out
Or a suggestion in this post that I should add references for the silent-redirect.html to my angular.json file, this didn't help
I am using Angular 7 and on Chrome Version 73.0.3683.86 (Official Build) (64-bit)
This one took me several tries to fix. It was a combination of:
my oidc-client.js UserManager object was getting initialized more than once;
I didn't have the /silent-refresh path for all my environments registered in my IdentityServer4 ClientRedirectUris table.
I usually got this error when I forget to configure the "silent refresh" URL.

No user in signinSilentCallback using identityserver and oidc client of javascript

I am getting user undefined in following code.
I have already authenticated user from MVC.
But when I use signinSilentCallback to get detail of that user, it is getting undefined using oidc-client in js.
It doesn't give any error as well.
var mgr = new UserManager({
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:50144/signin-oidc",
silent_redirect_uri: "http://localhost:50144/signin-oidc",
response_type: "id_token token",
post_logout_redirect_uri: "http://localhost:50144/signout-callback-oidc",
});
mgr.signinSilentCallback().then(function (user) {
//**Here user is undefined.**
axios.defaults.headers.common['Authorization'] = "Bearer " + user.access_token;
});
In Identityserver 4, client is defined as following.
new Client
{
ClientId = "js",
ClientName = "js",
ClientUri = "http://localhost:50144",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireClientSecret = false,
AccessTokenType = AccessTokenType.Jwt,
RedirectUris =
{
"http://localhost:50144/signin-oidc",
},
PostLogoutRedirectUris = { "http://localhost:50144/signout-callback-oidc" },
AllowedCorsOrigins = { "http://localhost:50144" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
}
}
signinSilentCallback: Returns promise to notify the parent window of response from the authorization endpoint.
https://github.com/IdentityModel/oidc-client-js/wiki
signinSilentCallback - This is not something will return you the user object.
If you really need to get the user object on silent renew i would suggest to use this approach with folloowing code snippet. This works for me in salesforce apps as well.
this.userManager.events.addAccessTokenExpiring(() =>
{
this.userManager.signinSilent({scope: oidcSettings.scope, response_type: oidcSettings.response_type})
.then((user: CoreApi.Authentication.Interfaces.OidcClientUser) =>
{
this.handleUser(user); // This function just set the current user
})
.catch((error: Error) =>
{
this.userManager.getUser()
.then((user: CoreApi.Authentication.Interfaces.OidcClientUser) =>
{
this.handleUser(user);
});
});
});
We need to handle the getUser in catch as well due to one of bug reported for iFrame in oidc-client js
From above code focus on the way the silent renew is performed when the token expires.
you can set automaticSilentRenew to true in your config
var mgr = new UserManager({
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:50144/signin-oidc",
silent_redirect_uri: "http://localhost:50144/signin-oidc",
response_type: "id_token token",
post_logout_redirect_uri: "http://localhost:50144/signout-callback-oidc",
automaticSilentRenew: true; //here
});
and you can use UserManager events to load the new user when the token is refreshed
this.mgr.events.addUserLoaded(args => {
this.mgr.getUser().then(user => {
this._user = user; // load the new user
});
});

How to pass passport facebook data to angular?

I am using passport facebook for user authentication in my web app. My Node backed is running on localhost:8080 and angular frontend is running on localhost:4200. How can I save the data received from Facebook, Save it to a database and then pass that database data to my angular frontend? I tried so many guides and tutorials online, all of those are running on the same domain, but mine is different domains(8080 & 4200).
Below is my social auth code, if it can be of any help.
module.exports = function(app, db) {
var express = require('express'),
ObjectID = require("mongodb").ObjectID,
passport = require('passport'),
FacebookStrategy = require('passport-facebook').Strategy,
GoogleStrategy = require( 'passport-google-oauth2' ).Strategy,
LinkedInStrategy = require('passport-linkedin');
var authConfig = require('../config/socialConfig');
var session = require('express-session');
app.use(passport.initialize());
app.use(passport.session());
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: false }
}))
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: authConfig.facebookAuth.clientID,
clientSecret:authConfig.facebookAuth.clientSecret ,
callbackURL: authConfig.facebookAuth.callbackURL,
profileFields: ['id', 'displayName', 'photos', 'email']
},
function(token, refreshToken, profile, done) {
console.log("Hello" + profile);
// User.findOrCreate(..., function(err, user) {
// if (err) { return done(err); }
// done(null, user);
// });
done(null, profile);
}
));
app.get('/auth/facebook/callback', passport.authenticate('facebook', { failureRedirect: '/login' }));
app.get('/auth/facebook', passport.authenticate('facebook', { scope: 'email' }));
}
And below is my frontend link to facebook auth
Facebook Login
Any help will be highly appreciated. Looking forward to some help, thanks in advance.
Since you Node.js app is on the other port, you need to specify the full URL to the /auth/facebook API (http://localhost:4020/auth/facebook).
Also, quoting the another post:
For two documents to be considered to have the same origin, the protocol >(http/https), the domain and the port (the default 80 or :xx) have to be >indentical
So you need to enable CORS on your node.js server. An easy way to do it is to use Express-cors npm
const cors = require('cors')
const app = express()
app.use(cors())

Multi Tenancy support for IdentityServer4 with javascript client

Hi I am using IdentityServer4 with aspnet core application.
I am using MVC client from their sample and also using a javascript client. The javascript client can be opened as tenantone.domain.com or tenanttwo.domain.com and so on according to tenancy name.
I am not able to get authorization for dynamic sub-domains.
Please help! if any one has done such task in asp.net core
To Register the javascript client I am using below code
string tenancyName = "tenantone"
var discoveryClient = new DiscoveryClient("http://login.domain.io:5000");
discoveryClient.Policy.RequireHttps = false;
var doc = await discoveryClient.GetAsync();
var request = new IdentityModel.Client.AuthorizeRequest(doc.AuthorizeEndpoint);
var url = new IdentityModel.Client.AuthorizeRequest(doc.AuthorizeEndpoint).CreateAuthorizeUrl(
clientId: tenancyName,
responseType: ResponseTypes.IdTokenToken,
scope: "openid profile api1",
redirectUri: "http://" + tenancyName + ".domain.io:5003/callback.html",
state: "random_state",
nonce: "random_nonce",
responseMode: "form_post",
extra: new
{
AllowedCorsOrigins = "http://" + tenancyName + ".domain.io:5003"
});
var response = new IdentityModel.Client.AuthorizeResponse(url);
var accessToken = response.AccessToken;
var idToken = response.IdentityToken;
var state = response.State;
Then after to be authorized from client I am using below code:
var config = {
authority: "http://login.domain.io:5000",
client_id: "tenantone",
redirect_uri: "http://tenantone.domain.io:5003/callback.html",
response_type: "id_token token",
scope: "openid profile api1",
post_logout_redirect_uri: "http://tenantone.domain.io:5003/",
};
var mgr = new Oidc.UserManager(config);
debugger;
mgr.getUser().then(function (user) {
if (user) {
//log("User logged in", user.profile);
$("#bodyPage").removeAttr("style");
}
else {
// log("User not logged in");
mgr.signinRedirect();
}
});

Resources