How do I get id_token for keycloak using OWIN? - c#

I'm working with an very old VB.net application trying to layer in SSO auth using OWIN and KeyCloak. This is all new to me. The approach I'm taking is to create a C# app to sit in between KeyCloak and my VB app. I've been able to get my C# app to open the login screen of KeyCloak, authenticate and return to the C# app or even the VB app. This seems fine.
However, I need the id_token and username to pass to the VB app. When using Fiddler I can see KeyCloak is generating a post back to my return page with the id_token in tow. However, it is on another thread and gets redirected to the original page but without the id_token. I must be missing something. I've seen code where there are notifications wired and I think they should grab the token and user info, but I don't know how to get the notifications to work. There is no explicit documentation to tell me what to do.
Am I supposed to have a listener or callback method to catch the post from KeyCloak? If so can some one show me how to create one?
Note: I've found some Microsoft code using OWIN and Azure and MVC that bring back user info. However, I point this same code to KeyCloak it authenticates but no user info is returned.
Any help will be greatly appreciated.
-Thanks
In my Startup.cs file I have the following (I've tried many different variations to no avail):
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = _clientId,
ClientSecret = _clientSecret,
RequireHttpsMetadata = false,
Authority = _authority,
RedirectUri = _redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = _redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
SecurityTokenReceived = OnSecurityTokenReceived
}
}
);
}

Related

Handle 404 exception in ASP.Net web API

I have ASP.Net web API in .net framework 4.8.
I have implemented single sign on using Azure active directory bearer token.
User will be redirected to login page if route is found in routing table. However, if user enters incorrect route (https://mysite/invalidurl) then it directly shows a yellow death screen with 404.
Is it possible to redirect user to login page first incase of invalid url (404) also? If yes then how? It means user enters any url (valid or invalid), s/he should be redirected to login page first.
I have already tried few options but none of the option worked for me like ExceptionHandler, AttributeRouting, CustomError in Web.Config, Application_Error in global.asax, global pattern matching in route table ( like this: routeTemplate: "api/{*uri}",)
Here is the code of my startup file
public void ConfigureAuth(IAppBuilder app)
{
List<string> audiences = ConfigurationManager.AppSettings["ida:Audience"].Split(AudienceDelimitter).ToList();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
AuthenticationType = "OAuth2Bearer",
Tenant = ConfigurationManager.AppSettings["ida:TenantId"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = audiences
}
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri
});
}
Thanks in advance.

How to reject token with invalid claims

I have an application ASP.net 4.X it uses UseOpenIdConnectAuthentication for authentication to an Identity server 4 application.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = identityServerUrl,
ClientId = clientId,
Scope = "openid profile testapi",
ClientSecret = secret,
RedirectUri = basePath + "/signin-oidc",
PostLogoutRedirectUri = basePath,
ResponseType = "id_token token code",
SignInAsAuthenticationType = "Cookies",
UseTokenLifetime = false,
....................
All of this works fine currently. However I have an issue. If something happens in the Identity server and it does not return all of the claims in the token that i need. I need a way to refuse it without ending in some kind of endless loop of login which i currently have.
I was wondering if there is some place in the middlewear that i can add something like
var needed= claims.FirstOrDefault(c => c.Type == "NeedThisClaim")?.Value;
If its not there then kick it out and refuse the user access. I was thinking of putting it in SecurityTokenValidated but I am not sure that's the best place or not. I am also not sure how to reject the token gracefully.
How do you verify that a token contains the required claims when authenticating an application.

OpenIdConnect redirects as if the user wasn't logged in

I'm working on a MVC Project for a customer (let's call this "Project A") and we have to use OpenIdConnect for authorization which I basically got it working, I will be redirected to the login site if i'm not authorized and so on. But if i want to go to an Action with [Authorize], it makes a request to the OpenIdConnect-Authority to see if i'm logged in and then redirects me to the RedirectUrl which is specified for this Client.
So if I want to go to /Home/Contact (in my example with [Authorize]) I can't get there, because i will always be redirected to my "RedirectUrl".
Do I have to store the Requested Url by myself and then redirect myself?
It doesn't matter if I set the RedirectUrl or not. I think it's a bit strange, because in another Project B we go against our Identity Server 4 and there it works without any problems.
This is the code in my Startup.Auth.cs:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
RedirectUri = redirectUri,
ResponseType = "code",
Scope = "openid profile email",
Configuration = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration
{
AuthorizationEndpoint = authority + "as/authorization.oauth2"
},
});
I had to set the AuthorizationEndpoint by myself, because it took always a wrong url. The difference here between Project A and Project B is, that the "ResponseType" is set to "code" in Project A. In Project B it is set to "id_token". But i have to set it to "code" because other Response Types are not accepted.
In Project B i also noticed that the ".AspNet.Cookies" is available on my site.
Which is not the case in Project A. Do i have to create it on my own?

Web Forms Authentication with B2C

I'm trying to add authentication using Azure AD B2C to a web forms app. Unfortunately, every tutorial I've found is for MVC, except for this web forms tutorial. Using that tutorial, I've added this code to my startup.auth.cs:
public partial class Startup {
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301883
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = "my-client-id",
Authority = "https://login.microsoftonline.com/my-tenant"
});
}
}
And that is working fine. However, I need to have sign up functionality as well as just sign-in, but I can't figure out how to do it, since everything I've found is for MVC, and I'm not sure how to convert that to what I need. I've tried adding code such as this:
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(_SignUpPolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(_ProfilePolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(_SignInPolicyId));
And that creates three more buttons on the login page, but clicking on them just gives a 404 error and no extra information, so I don't know how to make that work, either, or even if I'm headed in the right direction. I've never worked with B2C before, so if anyone has any suggestions/has done this sort of thing for web forms, I'd really appreciate some tips or sample code.
The example you are using is using "Local Accounts"
Local Accounts mean a local database, and for each Idenity provider it will add a button.
Try to change the authentication to "No Authentication" (and add all the files yourself) or "Work and School Accounts" (which connects to an AD, so convert that to B2C).
You will see a redirect to the https://login.microsoftonline.com/yourtenant.onmicrosoft.com/....
The next steps are to follow the same steps as with the MVC example, implement the same pieces of code.
Make sure to update the nuget packages to a newer version(1.0 and 4.0 are the default):
<package
id="Microsoft.IdentityModel.Protocol.Extensions"
version="1.0.2.206221351"
targetFramework="net46" />
<package
id="System.IdentityModel.Tokens.Jwt"
version="4.0.2.206221351"
targetFramework="net46" />
And the code:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(signInPolicyId));
}
private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
MetadataAddress = string.Format(aadInstance, tenant, policy),
AuthenticationType = policy,
ClientId = clientId,
RedirectUri = "https://localhost:44300/",
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
},
Scope = "openid",
ResponseType = "id_token",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
},
};
}
Add a /Account/SignIn.aspx page, and in the code behind place the code from the MVC sample SignIn:
if (!Request.IsAuthenticated)
{
// To execute a policy, you simply need to trigger an OWIN challenge.
// You can indicate which policy to use by adding it to the AuthenticationProperties using the
// PolicyKey provided.
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties()
{
RedirectUri = "/",
},
appConfiguration.B2CSignInPolicyId);
}

OWIN OpenID Connect Middleware Not Replacing Current User with ClaimsPrincipal

I have an existing MVC5 application I am converting from using AspNetIdentity to utilize ThinkTecture Identity Server 3 v2. The OpenID provider is not the biggest issue I'm having, as it seems to be working great. The security token is validated and I'm handling the SecurityTokenValidated notification in a method in order to get additional user info claims and add system-specific permission claims to the claim set, similar to the code below:
OWIN Middleware
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44300/identity",
Caption = "My Application",
ClientId = "implicitclient",
ClientSecret = Convert.ToBase64String(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("secret"))),
RedirectUri = "http://localhost:8080/",
ResponseType = "id_token token",
Scope = "openid profile email roles",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = ClaimsTransformer.GenerateUserIdentityAsync
}
});
Claims Transformer
public static async Task GenerateUserIdentityAsync(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var identityUser = new ClaimsIdentity(
notification.AuthenticationTicket.Identity.Claims,
notification.AuthenticationTicket.Identity.AuthenticationType,
ClaimTypes.Name,
ClaimTypes.Role);
var userInfoClient = new UserInfoClient(new Uri(notification.Options.Authority + "/connect/userinfo"),
notification.ProtocolMessage.AccessToken);
var userInfo = await userInfoClient.GetAsync();
identityUser.AddClaims(userInfo.Claims.Select(t => new Claim(t.Item1, t.Item2)));
var userName = identityUser.FindFirst("preferred_username").Value;
var user = MembershipProxy.GetUser(userName);
var userId = user.PersonID;
identityUser.AddClaim(new Claim(ClaimTypes.Name, userId.ToString(), ClaimValueTypes.Integer));
// Populate additional claims
notification.AuthenticationTicket = new AuthenticationTicket(identityUser, notification.AuthenticationTicket.Properties);
}
The problem is that the ClaimsIdentity assigned to the authentication ticket at the end of the transformer is never populated in the System.Web pipeline. When the request arrives to my controller during OnAuthenticationChallenge, I inspect the User property to find an anonymous WindowsPrincipal with a similar WindowsIdentity instance assigned to the Identity property, as if the web config's system.web/authentication/#mode attribute were set to None (at least I believe that's the behavior for that mode).
What might cause a failure by the middleware to set the principal for the user, or for it to be replaced during System.Web's processing with an anonymous Windows identity? I haven't been able to track this down.
EDIT: This occurs irrespective of whether the SecurityTokenValidated notification is handled and claims augmented.
EDIT 2: The cause appears to be making use of ASP.NET State Service session state server in my web.config with cookies. The configuration entry is:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="30" cookieless="UseCookies" />
This looks to be related to a reported issue in Microsoft.Owin.Host.SystemWeb #197 where cookies are not persisted from the OWIN request context into the System.Web pipeline. There are an assortment of workarounds suggested but I'd like someone to authoritatively point me to a properly vetted solution for this problem.
If you were to capture a fiddler trace, do you see the any .AspNet.Cookies.
I suspect the cookies are not being written. You can use a distributed cache and do away with cookies.
I think you need to include cookie authentication middleware because .AspNet.Cookies thing is written by that middleware. This is how you can integrate that middleware
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Note: Please make sure it should be on top of openid connect middleware
for more details about CookieAuthenticationOptions please goto this link https://msdn.microsoft.com/en-us/library/microsoft.owin.security.cookies.cookieauthenticationoptions(v=vs.113).aspx.

Categories