Using Resource Owner Flow to issue a cookie - identityserver3

I've read through similar issues, but am not finding my use case. We have an older MVC application using AspNetIdentity Forms Authentication. We are starting to transition parts of the site over to a new system that uses the IdentityServer3 IdP. From a user's prospective they log into the "old" site. Further into the site they may be redirected to the new site. Currently when this happens they have to log in again to the SSO page to get the new SSO cookie. We are trying to alleviate the dual signin.
On the back end the user's accounts are synchronized between the two authentication databases so their usernames and passwords are always in sync. To reduce the dual signin I was attempting to use the Resource Owner flow at the "old" site's signin page so it performs the Forms Auth signin first then uses Resource Owner to pass the credentials over to the IdentityServer for authentication with the new system. I figure if the SSO server auth happens at this time a cookie can be built so later when they navigate to the SSO protected sites they already have a valid cookie. The problem I'm finding is that the Resource Owner method doesn't seem to issue cookies. I created a MVC controller on the IdentityServer hosting website that performs the Resource Owner authentication. I have the "old" site opening a new browser tab to this page so that the HttpGet controller response will return to the browser if the Set-Cookie is issued. The Resource Owner auth works, I get an access token and the userInfo get works as well. I build up the ClaimsIdentity and SignIn using the OWIN authentication manager. The claims principal shows the user is authenticated, but I'm not getting a cookie. Is there a way to make this happen.
In summary, using their plaintext credentials at "old" system signin I want to pre-authenticate them with SSO to get a cookie so later navigation to SSO sites doesn't ask for a login.
Here is the Resource Owner Auth Get controller.
public ActionResult LoginGet(string username, string password) {
try {
username = HttpUtility.UrlDecode(username);
password = HttpUtility.UrlDecode(password);
//create identityserver sso cookie as well
var tokenClient = new TokenClient(
"https://localhost.fiddler:44333/core/connect/token",
"clientId",
"secret"
);
var scopes = "openid profile sampleApi roles";
var token = tokenClient.RequestResourceOwnerPasswordAsync(username, password, scopes).Result;
if (token != null && !String.IsNullOrWhiteSpace(token.AccessToken)) {
var claims = new List<Claim>();
var claimsClient = new UserInfoClient(new Uri("https://localhost.fiddler:44333/core/connect/userinfo"), token.AccessToken);
var userInfo = claimsClient.GetAsync().Result;
userInfo.Claims.ToList().ForEach(t => claims.Add(new Claim(t.Item1, t.Item2)));
claims.Add(new Claim("token", token.AccessToken));
var claimsId = new ClaimsIdentity(claims, "Cookies");
Request.GetOwinContext().Authentication.SignIn(claimsId);
ViewBag.Worked = true;
ClaimsPrincipal cp = Request.GetOwinContext().Authentication.User;
if(cp != null) {
ViewBag.IsAuthed = cp.Identity.IsAuthenticated;
}
return View();
}
} catch (Exception ex) {
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
return View();
}

The call to the token endpoint in a API/backchannel call. For setting an SSO cookie, you must redirect the browser to IdentityServer.
IOW - what you want to do is not possible without a browser redirect.

To reduce the dual signin I was attempting to use the Resource Owner flow at the "old" site's signin page so it performs the Forms Auth signin first then uses Resource Owner to pass the credentials over to the IdentityServer for authentication with the new system. I figure if the SSO server auth happens at this time a cookie can be built so later when they navigate to the SSO protected sites they already have a valid cookie.
You can't access or set cookies for a path(IdentityServer here) from another path even within the same host name (there sure are tricky ways to do that but of course, you'd know what to set, etc). See https://security.stackexchange.com/q/12439
In summary, using their plaintext credentials at "old" system signin I want to pre-authenticate them with SSO to get a cookie so later navigation to SSO sites doesn't ask for a login.
A very simple way to achieve this is to use the authorize endpoint that allows to pass additional authentication related information to the user service.
https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html. Then, on landing on the IdentityServer's sign in page, you can retrieve the user's identity.
To keep things simple:
From your "old" system, encrypt user's details or provide some form of unique hash
Include hash in authorization endpoint parameters when redirecting to IdentityServer or OWIN middleware
Grab this added parameter (hash) in your Custom UserService and retrieve user details from hash
From within your custom UserService, Populate the signin fields providing a user friendly message to proceed,
-- OR create a custom ViewService submitting the form dynamically
-- OR a custom LoginPage. Check here for samples

Related

What does it mean to "redirect with token" for single sign on?

(Background: I am trying to use my website hosted on wix as a simple identity provider so my members can access a separate sveltekit app I am creating--without logging in again--on a separate server because I do not think I can create the app on the wix platform. Basically I just need the user id, but I would like to also ensure they are in fact authenticated on my Wix hosted site before granting access).
In multiple pages explaining single sign on, it is explained that when my browser requests a protected resource from a web server, the server can (if it is configured to do so) verify my identity via a separate identity provider. This is done via a redirect to the identify provider. If I am not authenticated by the identify provider, I am asked to authenticate (by entering username and password, or whatever).
Once I am authenticated (by logging in or by verifying the presence of a valid session id on the identify provider's server from a prior login), the identify provider then "redirects with token" or a "token can be passed to the original domain by a redirect" according to these web sites I have encountered.
But what does it mean to "redirect with token"? This conflicts with other reading I have done which points out that redirects cannot have authentication or other headers or data associated with them.
How does it come to pass that (1) the web server I made my original request from gets my token from the identify provider while at the same time (2) returning my requested resource to my browser instead of back to the identity provider's server?
"Redirect with token" is a common method used in single sign-on (SSO) systems to authenticate users. In this method, when a user tries to access a protected resource on a server, the server redirects the user's browser to the SSO login page, along with a token that identifies the resource being accessed and the server that is requesting authentication.
The user then enters their login credentials on the SSO login page. If the credentials are correct, the SSO system authenticates the user and sends them back to the original server, along with a token that indicates that the user has been authenticated. The server checks the token to confirm that the user has been authenticated, and if the token is valid, the user is granted access to the protected resource.
Redirecting with a token is a secure and efficient way to authenticate users across multiple servers, as it allows the servers to rely on the SSO system to authenticate users and eliminates the need for each server to store and manage its own set of login credentials.

Keycloak Autenticator SPI to override redirect_uri

Does Keycloak Authenticator SPI able to override the redirect_uri after successful login to desired url?
Right now after successful login user will be redirected to the url they requested before, In my organization we have a use case where a group of users (external user) can login to the application but only able to view specific frontend client.
We already add extra user attribute "eligibleApp" to the external user, and we will use this attribute to redirect to its url regardless of what the user requested before.
We also map "eligibleApp" attribute to the jwt token so that the frontend application can use this to redirect their eligible application.
We haven't found any documentation about how we suppose to do this in the Keycloak Authenticator SPI, we only able to add some logic to function authenticate() and action() describe in https://www.keycloak.org/docs/latest/server_development/#implementing-an-authenticator

Identity Server 4 External IDP Signout Tidyup

I have Identity Server 4 running ok performing local authentication for an MVC app working fine. I needed to add support for an external IDP too so I followed the instructions in the documentation and based it on the quickstart code. So its currently configured to use the demo IDP at https://demo.identityserver.io as an external IDP and works fine for login - the user gets redirected to the external IDP for entering their details, my auth server gets back an id_token with user id (subject) which I match to a user in our own user repository. Our auth server then continues the login as per normal issuing its own tokens etc. - so all fine so far.
The problem I have is Sign-Out from the external IDP - if a user signs out from the demo.identityservier.io IDP directly, I need to ensure this filters back to clear up the stored authentication cookies thus requiring the user to sign in again if they attempt to access a protected page in the app.
This works ok if the user logs out of our own ID Server (i.e. the logout page presented by the ID Server has built-in iframes that ensure the MVC app gets tidied up). For the external IDP I would expect a similar thing, but cant see anything.
Here's the startup config registering the external IDP within our local IDP startup.
.AddOpenIdConnect("Ext_oidc", "Ext OpenID Connect", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://demo.identityserver.io/";
options.ClientId = "implicit";
options.ResponseType = "id_token";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
Any suggestions would be greatfully received
Have you provided an end session endpoint URL to the external IDP? If it's defined I'd expect it to be called as part of the explicit sign out process on the external IDP.
In your MVC app use below code in an action an call it to sign out and clear app cookie and ID Server cookies.
Request.GetOwinContext().Authentication.SignOut(Request.GetOwinContext().Authentication.GetAuthenticationTypes()
.Select(o => o.AuthenticationType).ToArray());

Programatically create an OpenID Connect id_token with IdentityServer3

I am adding IdentityServer3 on to an existing website (NopCommerce to be specific). It has it's own registration and authentication system, but we need to also offer OpenID Connect so that a back end application can be integrated. Calls to the back end need to have an id_token for the current user that the back end validates to confirm the identity.
I've found information about how to use an existing membership database to provide the user data for IdentityServer3 to check, however I am bit stuck on how to generate the id_token for each user. I guess the obvious answer is to replace the website login with IdentityServer, however that raises further issues for the rest of the project. Ideally I'd like the user to log in as normal and then call a method in IdentityServer to generate the id_token.
Is this possible? I've been hunting around, but can't find anything so far. The best I found was an answer to programmatically sign in to identityserver3. I think it's suggesting making a HTTP post to IdentityServer, but it feels kind of hacky.
I also found Implementing OAuth 2.0 and OpenId Connect provider using IdentityServer3 with existing login server and membership provider, but I have to admit it's assuming quite a bit of knowledge I don't have (yet).
My solution based on John C's answer worked using a NopCommece external authentication plugin, but I was unhappy with having to leave Nop to authenticate and register when IdentityServer was using the Nop database. Going via the external auth route seemed to be the only way to get an OpenID Connect id_token. After a break and some time to revisit the code though I found the following:
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
https://identityserver.github.io/Documentation/docsv2/configuration/serviceFactory.html
By implementing custom services, IdentityServer allows you to mess with the token creation and generation AND the dependency injection system it uses gives you access to instatiated versions of the default services.
Previously I had followed an answer which sent a username and password to the token endpoint. The OpenID specs say that this should only return the access_token, which is exactly what DefaultTokenService in IdenttyService does. By adding a CustomTokenResponseGenerator however, I was able to re-use the request to create and return an id_token too.
CustomTokenResponse class:
internal class CustomTokenResponseGenerator : ICustomTokenResponseGenerator
{
protected ITokenService _tokenService;
public CustomTokenResponseGenerator(ITokenService tokenService)
{
_tokenService = tokenService;
}
public Task<TokenResponse> GenerateAsync(ValidatedTokenRequest request, TokenResponse response)
{
var tokenRequest = new TokenCreationRequest
{
Subject = request.Subject,
Client = request.Client,
Scopes = request.ValidatedScopes.GrantedScopes,
//Nonce = request.AuthorizationCode.Nonce,
ValidatedRequest = request
};
var idToken = _tokenService.CreateIdentityTokenAsync(tokenRequest);
idToken.Wait();
var jwt = _tokenService.CreateSecurityTokenAsync(idToken.Result);
response.IdentityToken = jwt.Result;
return Task.FromResult(response);
}
}
How to inject the custom service in Startup.cs:
factory.TokenService = new Registration<ITokenService, TokenService>();
factory.CustomTokenResponseGenerator = new Registration<ICustomTokenResponseGenerator, CustomTokenResponseGenerator>();
When user logs-in into NopCommerce application, you can send an HTTP authorize request to identityserver. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the id_token or access_token without showing a consent to the user again if the user is already logged-in.
function getAuthorizeRequest() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/NopCommerceapp&scope=siteBscope openid email roles&prompt=none&nonce="76767xz676xzc76xz7c67x6c76"
return encodeURI(url);}
Checkout idsrv authorize endpoint https://identityserver.github.io/Documentation/docsv2/endpoints/authorization.html
I think your best solution would be to implement IdentityServer3 and get it reading from your existing Nop membership database. Then create a Web API app that runs on its own that utilizes your IdentityServer3 setup. Inside the Web API, you implement all the functionality that your back end app needs, reading and writing to the Nop database.
Keep your Nop frontend UI separate from your backend API. If you follow the two links below, you should be able to get something up and running pretty quickly.
Creating the simplest OAuth2 Authorization Server, Client and API
MVC Authentication & Web APIs

Facebook login in JWT

I have developed token based spring security using JWT referring this project in git https://github.com/szerhusenBC/jwt-spring-security-demo. Now I need to get facebook login in my application. For social login, I found another web page https://ole.michelsen.dk/blog/social-signin-spa-jwt-server.html which explains how the social login must be carried out.
In the normal login, my JWT project creates a token based on username, password, expiry date and time of creation. Everytime the token comes, all values from above fields are retrieved and compared to authenticate the token and then served. I've two questions:
In the social login, there will be no password created. A token will be received from the facebook(my frontend does this). I have to verify if the token is valid or not. How am I supposed to do it in JWT?
After verifying as per the article I'm supposed to create my own token for future reference. Now, there is no password in facebook login. How do I create the token?
Let me know if there are any good site available for social login using JWT in spring boot applictaion.
I found myself in similar situation, and decided to follow a slightly different approach, delegating the responsibility of authenticating with FB to the server itself.
It provides an entry point: “/auth/facebook” that redirects to FBs and proceeds to the authentication.
After that it acquires the AccessToken for the logged user and creates a JWT Token that returns to the client.
Here is a blog post explaining how to use Spring Social Facebook and Spring Security for a similar case: Stateless Spring Security Part 3: JWT + Social Authentication
Consider removing the password field from your jwt. Facebook can supply you the email and name so use that for the payload. Here is my example.
userSchema.methods.generateJwt = function() {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign(
{
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
},
jwt_secret
);
};