jxycms
8/13/2018 - 5:00 AM

identity server 4

*******in server**********

config.cs

public class Config
{
    private readonly IConfiguration _configuration;

    public Config(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    // scopes define the resources in your system
    public IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
        };
    }

    public IEnumerable<ApiResource> GetApiResources(string companyCode, bool isTest)
    {

        return new List<ApiResource>
        {
            new ApiResource(){
                Name = "APIServiceHUB",
                ApiSecrets =
                {
                    new Secret(GetSecret(companyCode, isTest).Sha256())
                },
                Scopes =
                {
                    new Scope()
                    {
                        Name = "APIServiceHUB",
                        DisplayName = "APIService HUB",
                    }
                }
            },

            new ApiResource("projects-api", "Projects API")
        };
    }

    // clients want to access resources (aka scopes)
    public IEnumerable<Client> GetClients(string companyCode, bool isTest)
    {
        var optionsBuilder = new DbContextOptionsBuilder<DickerDataContext>();
        DickerDataContext dickerDataContext = new DickerDataContext(optionsBuilder.Options);
        // client credentials client
        return new List<Client>
        {
            new Client
            {
                ClientId = "client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
                ClientName =  "APIService HUB",
                RedirectUris = new List<string>{ "http://dev-webservices.dickerdata.com.au/signin-oidc" },
                RequireConsent = false,
                AllowOfflineAccess = true,
                //Access token life time is 14400 seconds (4 hours)
                AccessTokenLifetime = 14400,
                //Identity token life time is 14400 seconds (4 hours)
                IdentityTokenLifetime = 14400,
                ClientSecrets =
                {
                    new Secret(GetSecret(companyCode, isTest).Sha256())
                },
                AllowedScopes =
                {
                    "APIServiceHUB"
                }
            },


            // OpenID Connect implicit flow client (MVC)
            new Client
            {
                ClientId = "mvc",
                ClientName = "MVC Client",
                AllowedGrantTypes = GrantTypes.Implicit,

                RedirectUris = { "http://localhost:60948/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:60948/signout-callback-oidc" },

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile
                }
            }
        };
    }

    private string GetSecret(string companyCode, bool isTest)
    {
        var optionsBuilder = new DbContextOptionsBuilder<DickerDataContext>();
        optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DickerDataConString"));
        DickerDataContext dickerDataContext = new DickerDataContext(optionsBuilder.Options);
        string secret = dickerDataContext.SiteConfig.Where(x => x.Vendor == "SERVICEHUB" && x.CompanyCode == companyCode && (x.IsTest ?? false) == isTest).FirstOrDefault().Value;
        return secret;
    }
}

in startup.cs

services.AddIdentityServer()
  .AddDeveloperSigningCredential()
  .AddInMemoryApiResources(Config.GetApiResources())
  .AddInMemoryClients(Config.GetClients());


in Service file

public TokenModelResponse GetToken(string clientId, string secret)
{
    string url = _dBManager.GetURL(APIList.SH_DD_Token);
    TokenModel inputModel = new TokenModel()
    {
        ClientId = clientId,
        Secret = secret
    };
    var token = ServiceHubJsonExtractor.GetJsonResult<TokenModelResponse>("http://localhost:51678/api/Token/Authenticate", WebMethods.GET, inputModel, null, null);
    return token;
}

public class ServiceHubJsonExtractor
{
    public static T GetJsonResult<T>(string baseUrl, WebMethods method, 
                                    dynamic queryParameters = null, 
                                    dynamic bodyContent = null, 
                                    dynamic token = null, 
                                    string contentType = "application/json") where T : new()
    {
        string url = string.Empty;
        if (queryParameters != null)
        {
            url = BuildUrl(baseUrl, queryParameters);
        }
        else
        {
            url = baseUrl;
        }
        var request = (HttpWebRequest)HttpWebRequest.Create(url);

        request.Accept = "application/json";
        request.Method = method.ToString();
        request.ContentType = contentType;

        if (token != null)
        {
            request.Headers.Add("Authorization", $"{token.Token_Type} {token.Access_Token}");
        }

        var settings = new JsonSerializerSettings
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            Converters = { new JsonExtractor.StringOrArrayConverter() },
            Error = (sender, args) =>
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    System.Diagnostics.Debug.Write(args.CurrentObject.ToString());
                    System.Diagnostics.Debugger.Break();
                }
            }
        };

        try
        {
            if (bodyContent != null)
            {
                string contentString = string.Empty;
                if (bodyContent is String)
                {
                    contentString = bodyContent;
                }
                else
                {
                    contentString = JsonConvert.SerializeObject(bodyContent);
                }
                using (var writer = new StreamWriter(request.GetRequestStream()))
                {
                    writer.Write(contentString);
                }
            }
            var response = request.GetResponse();
            using (var reader = new StreamReader(response.GetResponseStream()))
            {
                var responseContent = reader.ReadToEnd();

                var result = new T();

                try
                {
                    if (!string.IsNullOrWhiteSpace(responseContent))
                        result = JsonConvert.DeserializeObject<T>(responseContent, settings);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                return result;

            }
        }
        catch (WebException webException)
        {
            string error = string.Empty;
            try
            {
                var errroResponse = new StreamReader(webException.Response.GetResponseStream());
                error = errroResponse.ReadToEnd();
            }
            catch
            {
                throw webException;
            }

            throw new Exception(error);

        }
    }

    private static string BuildUrl(string baseUrl, dynamic queryParameters)
    {
        string url = string.Empty, parameterList = string.Empty;
        if (queryParameters != null)
        {
            try
            {
                // the parameters could be dictionary or list

                Type type = queryParameters.GetType();
                if (type.Name == "Dictionary`2" || type.Name == "List`1") // dictionary/list
                {
                    foreach (KeyValuePair<string, string> param in queryParameters)
                    {
                        if (parameterList.Length > 0) parameterList += "&";
                        parameterList += string.Format("{0}={1}", param.Key, param.Value);
                    }
                }
                else // class with properties
                {
                    foreach (PropertyInfo propertyInfo in type.GetProperties())
                    {
                        if (propertyInfo.CanRead)
                        {
                            if (parameterList.Length > 0) parameterList += "&";
                            parameterList += string.Format("{0}={1}", propertyInfo.Name, propertyInfo.GetValue(queryParameters, null));
                        }
                    }
                }
            }
            catch (JsonException jex)
            {
                throw jex;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        if (!string.IsNullOrWhiteSpace(parameterList))
        {
            url = string.Format("{0}?{1}", baseUrl, parameterList);
        }
        else
        {
            url = baseUrl;
        }

        return url;
    }

}


*******in api project********

*******Fetch token from other server:

private async Task<TokenModelResponse> FetchToken(string clientId, string secret)
{
    var disco = await DiscoveryClient.GetAsync("http://localhost:60948");
   
    //var response = new AuthorizeResponse("http://localhost:60948");
    if (disco.IsError)
    {
        Console.WriteLine(disco.Error);
        return new TokenModelResponse { Message = "Fail to get token from server" };
    }
    // request token
    var tokenClient = new TokenClient(disco.TokenEndpoint, clientId, secret);
    var tokenResponse = await tokenClient.RequestClientCredentialsAsync("APIServiceHUB");
    if (tokenResponse.IsError)
    {
        Console.WriteLine(tokenResponse.Error);
        return new TokenModelResponse { Message = "Fail to get token from server" };
    }
    var introspectionClient = new IntrospectionClient(
        disco.IntrospectionEndpoint,
        "APIServiceHUB",
        secret);
    var introspectionResponse = await introspectionClient.SendAsync(
                        new IntrospectionRequest { Token = tokenResponse.AccessToken });
    if (introspectionResponse.IsError)
    {
        Console.WriteLine(introspectionResponse.Error);
        return new TokenModelResponse { Message = "Fail to get token from server" };
    }
    TokenModelResponse tokenModelResponse = new TokenModelResponse();
    tokenModelResponse.Access_Token = tokenResponse.AccessToken;
    tokenModelResponse.Token_Type = tokenResponse.TokenType;
    tokenModelResponse.Expires_In = tokenResponse.ExpiresIn;
    tokenModelResponse.Issured = ToDateTimeFromEpoch(long.Parse(introspectionResponse.Json.GetValue("nbf").ToString())).ToString("r");
    tokenModelResponse.Expires = ToDateTimeFromEpoch(long.Parse(introspectionResponse.Json.GetValue("exp").ToString())).ToString("r");
    tokenModelResponse.Message = "Successfully get token";
    return tokenModelResponse;
}

private DateTime ToDateTimeFromEpoch(long intDate)
{
    var timeInTicks = intDate * TimeSpan.TicksPerSecond;
    return new DateTime(1970, 1, 1, 10, 0, 0, 0, DateTimeKind.Local).AddTicks(timeInTicks);
}

[HttpGet]
public async Task<IHttpActionResult> Authenticate() // controller method to get token
{
    var loginResponse = new LoginResponse { };
    //LoginRequest loginrequest = new LoginRequest { };
    //loginrequest.Username = login.Username.ToLower();
    //loginrequest.Password = login.Password;

    IHttpActionResult response;
    HttpResponseMessage responseMsg = new HttpResponseMessage();
    bool isUsernamePasswordValid = true;

    //if(login != null)
    //isUsernamePasswordValid=loginrequest.Password=="admin" ? true:false;
    // if credentials are valid
    if (isUsernamePasswordValid)
    {
        string token = await FetchToken();
        //return the token
        return Ok(token);
    }
    else
    {
        // if credentials are not valid send unauthorized status code in response
        loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
        response = ResponseMessage(loginResponse.responseMsg);
        return response;
    }
}

*******login response model

public class TokenModel
{
    public string ClientId { get; set; }
    public string Secret { get; set; }
}
public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

public class LoginResponse
{
    public LoginResponse()
    {

        this.Token = "";
        this.responseMsg = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.Unauthorized };
    }

    public string Token { get; set; }
    public HttpResponseMessage responseMsg { get; set; }

}

public class TokenModelResponse
{
    public string Access_Token { get; set; }
    public string Token_Type { get; set; }
    public long Expires_In { get; set; }
    public string UserName { get; set; }
    public string FullName { get; set; }
    public string Logo { get; set; }
    public string ChannelPortalId { get; set; }
    public string Permissions { get; set; }
    public string WorkflowUserGroup { get; set; }
    public string LogonId { get; set; }
    public string Issured { get; set; }
    public string Expires { get; set; }
}

*******validate token:

using IdentityModel;
using IdentityModel.Client;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

internal class TokenValidationHandler : DelegatingHandler
{
    const string auth0Domain = "http://localhost:50191/";
    private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authzHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authzHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpStatusCode statusCode;
        string token;
        //determine whether a jwt exists or not
        if (!TryRetrieveToken(request, out token))
        {
            statusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return await base.SendAsync(request, cancellationToken);
        }

        try
        {
            //const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
            //var now = DateTime.UtcNow;
            //var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            handler.InboundClaimTypeMap.Clear();
            var disco = await DiscoveryClient.GetAsync("http://localhost:60948");
            var keylist = new List<SecurityKey>();
            foreach (var webKey in disco.KeySet.Keys)
            {
                var exp = Base64Url.Decode(webKey.E);
                var mod = Base64Url.Decode(webKey.N);
                var key = new RsaSecurityKey(new RSAParameters() { Modulus = mod, Exponent = exp });
                keylist.Add(key);
            }
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = "http://localhost:60948/resources",
                ValidIssuer = "http://localhost:60948",
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,
                IssuerSigningKeys = keylist
            };

            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return await base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException e)
        {
            statusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            statusCode = HttpStatusCode.InternalServerError;
        }
        return await Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }

    private async Task<OpenIdConnectConfiguration> GetSigningKeys()
    {
        IConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{auth0Domain}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
        OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
        return openIdConfig;
    }
}

********API controller

add [Authorize] to controller class


_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________



*******create token and validate************

private string createToken(string username)
{
    //Set issued at date
    DateTime issuedAt = DateTime.UtcNow;
    //set the time when it expires
    DateTime expires = DateTime.UtcNow.AddDays(7);

    //http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
    var tokenHandler = new JwtSecurityTokenHandler();

    //create a identity and add claims to the user which we want to log in
    ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
    {
        new Claim(ClaimTypes.Name, username)
    });

    const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
    var now = DateTime.UtcNow;
    var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
    var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);


    //create the jwt
    var token =
        (JwtSecurityToken)
            tokenHandler.CreateJwtSecurityToken(issuer: "http://localhost:60948", audience: "http://localhost:60948/resources",
                subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
    var tokenString = tokenHandler.WriteToken(token);

    return tokenString;
}

internal class TokenValidationHandler : DelegatingHandler
{
    private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authzHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authzHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpStatusCode statusCode;
        string token;
                    //determine whether a jwt exists or not
        if (!TryRetrieveToken(request, out token))
        {
            statusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
            var now = DateTime.UtcNow;
            var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));                


            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = "http://localhost:50191",
                ValidIssuer = "http://localhost:5001",
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,                    
                IssuerSigningKey = securityKey                
            };
            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException e)
        {
            statusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            statusCode = HttpStatusCode.InternalServerError;
        }
        return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode){ });
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }

    
}