1

I've been attempting to implement a working solution into our .Net 4.5.2 web site project. My goal is to convert my web site project, which is a web forms application (not MVC), into using identity server 3 (possibly via thinktecture's implementation) to authenticate/authorize users.

Is this possible? How will the site life cycle change or can it say the same with a session object? Can I use identity server to gain the access token and then setup a session, and only request a new access token when the session expires or the access token expires?

Some Back Story:

I do have a working identity server 3 setup at the following location https://localhost:44399. Which responds with all the appropriate calls and limitations when I use a straight html file, but when I tried to implement the same thing in the login page (portal.aspx) it seems to forget the user and does not behave the same. The website (the client) is setup on https://localhost:9898. And the login page is https://localhost:9898/portal.aspx.

I'm using the oidc-client.js on the portal.aspx page, but I keep getting CORS (cross-browser origin scripting) errors when it is trying to authenticate. Is this an issue with web forms, this does not happen when using a regular html page?

This is a .net 4.5.2 "web site project" (not a "web application project") which uses web forms, not MVC. I have got many demo MVC applications to work correctly, but when I try to take the knowledge I have to the web forms application, I see that I really don't have a grasp on what is going on.

I have added the following nuget packages:

Microsoft.Bcl v1.1.10
Microsoft.Owin v3.0.1
Microsoft.Bcl.Async v1.0.168
System.IdentityModel.Tokens.Jwt v5.1.0
Newtonsoft.Json v9.0.1
Owin v1.0.0
Microsoft.IdentityModel.Logging v1.1.0
Microsoft.Owin.Host.SystemWeb v3.0.1
System.IdentityModel.Tokens.Jwt v5.1.0
Microsoft.IdentityModel.Tokens v5.1.0
Microsoft.Bcl.Build v1.0.21
Thinktecture.IdentityModel v3.6.1

When I attempt to add the startup.cs class it tells me that these classes are typically added to the App_Code directory, and do I want to put it there? I have tried both the root and the App_Code directory and neither seem to register when they are being called. I have added the package, thinking that this is what does the call:

Microsoft.Owin.Host.SystemWeb

But it seems like the startup class is not registered. So how do I get this to register in the pipeline so that 401 not authorized errors will send the user to the identity server?

Here is my startup class:

using ExpenseTracker.WebClient.Helpers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Newtonsoft.Json.Linq;
using Owin;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Web;
using System.Web.Helpers;
using Thinktecture.IdentityModel.Client;


[assembly: OwinStartup(typeof(ExpenseTracker.WebClient.Startup))]

namespace ExpenseTracker.WebClient
{
public class Startup
{

    public void Configuration(IAppBuilder app)
    {
        JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

        AntiForgeryConfig.UniqueClaimTypeIdentifier = "unique_user_key";

        app.UseResourceAuthorization(new AuthorizationManager());

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies"
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            ClientId = "mvc",
            Authority = ExpenseTrackerConstants.IdSrv,
            RedirectUri = ExpenseTrackerConstants.ExpenseTrackerClient,
            SignInAsAuthenticationType = "Cookies",

            ResponseType = "code id_token token",
            Scope = "openid profile roles expensetrackerapi offline_access",

            Notifications = new OpenIdConnectAuthenticationNotifications()
            {

                MessageReceived = async n =>
                {
                    EndpointAndTokenHelper.DecodeAndWrite(n.ProtocolMessage.IdToken);
                    EndpointAndTokenHelper.DecodeAndWrite(n.ProtocolMessage.AccessToken);

                    //var userInfo = await EndpointAndTokenHelper.CallUserInfoEndpoint(n.ProtocolMessage.AccessToken);    

                },

                SecurityTokenValidated = async n =>
                {                         
                    var userInfo = await EndpointAndTokenHelper.CallUserInfoEndpoint(n.ProtocolMessage.AccessToken);


                    // use the authorization code to get a refresh token 
                    var tokenEndpointClient = new OAuth2Client(
                        new Uri(ExpenseTrackerConstants.IdSrvToken),
                        "mvc", "secret");

                    var tokenResponse = await tokenEndpointClient.RequestAuthorizationCodeAsync(
                        n.ProtocolMessage.Code, ExpenseTrackerConstants.ExpenseTrackerClient);



                    var givenNameClaim = new Claim(
                        Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName,
                        userInfo.Value<string>("given_name"));

                    var familyNameClaim = new Claim(
                        Thinktecture.IdentityModel.Client.JwtClaimTypes.FamilyName,
                        userInfo.Value<string>("family_name"));

                    var roles = userInfo.Value<JArray>("role").ToList();

                    var newIdentity = new ClaimsIdentity(
                       n.AuthenticationTicket.Identity.AuthenticationType,
                       Thinktecture.IdentityModel.Client.JwtClaimTypes.GivenName,
                       Thinktecture.IdentityModel.Client.JwtClaimTypes.Role);

                    newIdentity.AddClaim(givenNameClaim);
                    newIdentity.AddClaim(familyNameClaim);

                    foreach (var role in roles)
                    {
                        newIdentity.AddClaim(new Claim(
                        Thinktecture.IdentityModel.Client.JwtClaimTypes.Role,
                        role.ToString()));
                    }

                    var issuerClaim = n.AuthenticationTicket.Identity
                        .FindFirst(Thinktecture.IdentityModel.Client.JwtClaimTypes.Issuer);
                    var subjectClaim = n.AuthenticationTicket.Identity
                        .FindFirst(Thinktecture.IdentityModel.Client.JwtClaimTypes.Subject);

                    newIdentity.AddClaim(new Claim("unique_user_key",
                        issuerClaim.Value + "_" + subjectClaim.Value));


                    newIdentity.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
                    newIdentity.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
                    newIdentity.AddClaim(new Claim("expires_at",  
                        DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));

                    n.AuthenticationTicket = new AuthenticationTicket(
                        newIdentity,
                        n.AuthenticationTicket.Properties);

                },


            }



            });


    }

}
}

And to make things worse, adding Microsoft.Owin.Host.SystemWeb does not compile due to strange errors like:

    The type or namespace name 'SessionState' does not exist in the 
    namespace 'System.Web' (are you missing an assembly reference?)
  • 1
    The "Thinktecture.Identity..." packages (and namespaces) are older ones, they took Thinktetcture out of the name several releases ago. – lgaud Dec 02 '16 at 12:03

0 Answers0