Nancy
Nancy copied to clipboard
Nancy and Okta OpenID Integration via OWIN
I'm using the latest version of NancyFX with .NET Framework 4.7.1 in a project to create a RESTful Service. I'm finding that I am not authenticating the client with OpenID via Okta. Below is my Startup.cs code. If anyone could advise on why this is not working?
using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Configuration;
using System.Security.Claims;
using IdentityModel.Client;
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using Nancy.Owin;
using Nancy;
[assembly: OwinStartup(typeof(ServiceFrameworkBase.Startup))]
namespace ServiceFrameworkBase
{
public class Startup
{
private readonly string clientId = ConfigurationManager.AppSettings["okta:ClientId"];
private readonly string redirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"];
private readonly string authority =ConfigurationManager.AppSettings["okta:OrgUri"];
private readonly string clientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"];
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
RedirectUri = redirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = OpenIdConnectScope.OpenIdProfile,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Exchange code for access and ID tokens
var tokenClient = new TokenClient(authority + "/v1/token", clientId, clientSecret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, redirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
var userInfoClient = new UserInfoClient(authority + "/v1/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
var claims = new List<Claim>();
claims.AddRange(userInfoResponse.Claims);
claims.Add(new Claim("id_token", tokenResponse.IdentityToken));
claims.Add(new Claim("access_token", tokenResponse.AccessToken));
if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
{
claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
}
n.AuthenticationTicket.Identity.AddClaims(claims);
return;
},
RedirectToIdentityProvider = n =>
{
// If signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenClaim != null)
{
n.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
}
}
return Task.CompletedTask;
}
},
});
}
}
}
What have you tried? How do you know it's not working? Have you stepped through the code? You need to give us a bit more to work with here...
Hello khellang. My apologies for the lack of details. I have stepped through the following code:
public class BaseModule : NancyModule
{
public BaseModule()
{
SetupLogger();
// Before allowing Request to go through, first check and see if end-user is Authentication
Before += ctx =>
{
return AuthenticateUser();
};
}
and found no authentication is occurring in the following:
private Response AuthenticateUser()
{
var user = HttpContext.Current.User.Identity;
// Check is user authenticated via AD
if (!user.IsAuthenticated)
{
// If not, see if the JWT is available
HttpRequest currentRequest = HttpContext.Current.Request;
if (currentRequest.Cookies.Get(OKTA_COOKIE_SESSION_NAME) != null)
{
string sessionValue = currentRequest.Cookies.Get(OKTA_COOKIE_SESSION_NAME).Value;
// TODO: Authenticate JWT Token
return null;
}
else
{
// If user does not have JWT, return access denied
return NotifyEndUserAccessDenied(false);
}
}
else
{
Logger.WriteLine("User has been proper authenticated as " + user.Name + " from the HTTP Address " + Request.UserHostAddress);
return null;
}
}
I am probably missing something very simple. My apologies if this is the case.
Hello,
I see from numerous articles that I should be able to use the method "app.UseNancy()" in the Startup.cs and that this should smooth out integration. I'm receiving the following error when I attempt to:
Severity Code Description Project File Line Suppression State Error CS1929 'IAppBuilder' does not contain a definition for 'UseNancy' and the best extension method overload 'DelegateExtensions.UseNancy(Action<Func<Func<IDictionary<string, object>, Task>, Func<IDictionary<string, object>, Task>>>, NancyOptions)' requires a receiver of type 'Action<Func<Func<IDictionary<string, object>, Task>, Func<IDictionary<string, object>, Task>>>' ServiceFrameworkBase ~\RESTServices\BaseFramework\WebApplication1\Startup.cs 32 Active
Has anyone ever encountered this before and know of a solution?
Have you installed Nancy.Owin?