AddOpenIdConnect options.Authority overriden with current host behind proxy (UseForwardedHeaders) - docker

I host my application behind reverse proxy inside container. I'm using IdentityServer4 and I'm trying to make oidc SSO with Azure AD. It works when the application is not using app.UseForwardedHeaders(opts), but I need them.
.NET Core version: 3.1
Forwarded headers configurations:
var opts = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost
};
opts.KnownProxies.Add(ipAddress);
app.UseForwardedHeaders(opts);
OpenID Connect Configurations:
services.AddAuthentication( options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}
)
.AddOpenIdConnect("aadrm", "Azure AD", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://login.microsoftonline.com/{tenantID...}/v2.0";
options.ClientId = ".....";
options.ClientSecret = #"........";
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
});
My Controller Action:
[HttpGet]
public IActionResult ExternalLoginChallenge(string provider, string returnUrl)
{
var callbackUrl = Url.Action(nameof(ExternalLoginCallback));
var props = new AuthenticationProperties
{
RedirectUri = callbackUrl,
Items =
{
{ "scheme", provider },
{ "returnUrl", returnUrl }
}
};
return Challenge(props, provider);
}
When I initiate the sign in process it should redirect me to the Microsoft's sign in page, but instead it redirects me back to my host with this url: https://mylocalapp.com:443/{tenantId}/oauth2/v2.0/authorize?client_id=...&redirect_uri=HTTPS%3A%2F%2Fmylocalapp.com%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=........&state=............&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=6.8.0.0
When I remove app.UseForwardedHeaders(opts) it works normally (redirects me to MS login page). It seems that UseForwardedHeaders overrides the OpenIdConnect Authority Address host.
Can you help me, I don't understand why it redirects me back to my host?
Thanks.

I've fount the issue and here is the solution, just in case someone is using IIS as reverse proxy in their local setup.
The issue was caused by the IIS reverse proxy configurations, that I'm using locally - Application Request Routing -> Server Proxy Settings -> Reverse rewrite host in response headers.

Related

JWT Same Application is the Auth Server and the Application Server

Hi experts I have recently explored a lot on OAuth and how JWT works. Essentially there should be an AuthServer that issues a token and there should be a ServiceAPI(Application Server) that uses the token for a client!!. I also understand a token is made up of 3-parts, the header, payload and signature...
Now what if I want to build an API that does both ...Authenticates and issues JWT tokens - and then provide the service afterwards..It Sounds like Basic Authentication with tokens!!
I am also unsure if the code I have written reflects this concept (that token issuer is the same as ServiceAPI). I am building a .net core 2.1 Web API following this article.
In the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "https://localhost:44387/";
options.Audience = "JWT:Issuer";
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("GuidelineReader", p => {
p.RequireClaim("[url]", "GuidelineReader");
});
});
}
I have also added a LoginController that generates the token and returns it...
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody]Application login)
{
IActionResult response = Unauthorized();
var user = AuthenticateUser(login);
if (user != null)
{
var tokenString = GenerateJSONWebToken(user);
response = Ok(new { token = tokenString });
}
return response;
}
private string GenerateJSONWebToken(Application appInfo)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
null,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
What is the difference in the following
options.Authority
options.Audience (I am thinking it is the application that sends https request)
options.Issuer
options.Authority
Authority is the address of the token-issuing authentication server. In your scenario the web api issue the token , so the Authority will be the url of web api .
options.Audience (I am thinking it is the application that sends https request)
Audience represents the intended recipient of the incoming token or the resource that the token grants access to. In your scenario ,the web api is the protected resource which client will access with JWT token , web api will validate the token to check the claims/signature . So web api name/URL should be the Audience
options.Issuer
Issuer Identifies the security token service (STS) that constructs and returns the token . In your scenario , the web api validates the user credential and return token. So web api name/URL is the Issuer

Asp.net Odata Web API Cross Origin Put and Pacth Request Browser Preflight Error

Shortly I ll try to describe my problem.
When I try my api actions with Postman, Fiddler everything works fine. But when it comes to browser Ajax Request 'OPTIONS' seems to my API not handling 'OPTIONS' method.
By the way, my 'Get' AND 'Post' action works like a charm on browser.
My attempts :
Enable Cors (OK) tried either web.config or global.asax
I tried Application_BeginRequest to handle OPTIONS requests. It returns OK but did not pass to my main Path/Put function.
Notes : My app currently in development environment. So my service and client is on localhost with different ports. I am in doubt that its about deployment issue.
Technologies
Asp.Net Web Api, Odata for Service Layer and react for client presentation layer.
I would be appreciated to suggestion.
Thanks.
I've just solved the problem.
Solution : Set Auth and app.UseCors(corsOptions); config before app.useWebApi() code line.
Full config located below. Really sad...
Startup.cs
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
HttpConfiguration configuration = new HttpConfiguration();
//Config
Configure(app);
WebApiConfig.Register(configuration);
app.UseWebApi(configuration);
}
private void Configure(IAppBuilder app)
{
var corsPolicy = new CorsPolicy
{
AllowAnyMethod = true,
AllowAnyHeader = true,
SupportsCredentials = true
};
// Try and load allowed origins from web.config
// If none are specified we'll allow all origins
// TODO get this value from web.config
var origins = "http://localhost:5911";
if (origins != null)
{
foreach (var origin in origins.Split(';'))
{
corsPolicy.Origins.Add(origin);
}
}
else
{
corsPolicy.AllowAnyOrigin = true;
}
var corsOptions = new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(corsPolicy)
}
};
app.UseCors(corsOptions);
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions()
{
TokenEndpointPath = new Microsoft.Owin.PathString("/getToken"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
AllowInsecureHttp = true,
Provider = new AuthorizationServerProvider() // Custom Provider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}

OpenIdConnect HTTP Basic authentication scheme for client authentication

I'm trying to implement an OpenIdConnect login to my .net core 2.0 site.
The IdentityServer I'm trying to use only supports 'client_secret_basic' as token_endpoint_auth_methods.
I configured the application as follows:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Cookies";
options.DefaultSignInScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.SignInScheme = "Cookies";
options.Authority = Auhtority;
options.ClientId = ClientID;
options.ClientSecret = ClientSecret;
options.ResponseType = "code";
options.SaveTokens = true;
options.Scope.Add("profile");
options.Scope.Add("rrn");
});
But this seems to post the ClientId and ClientSecret to the body of the request and not using HTTP Basic authentication.
I'm probably missing something obvious, but I can't seem to find how to configure the OpenIdConnect to use client_secret_basic instead of client_secret_post.
Any ideas?
I had the same issue and I finally got it to work using the following options / event hook using System.Net.Http;:
options.Events.OnAuthorizationCodeReceived = context =>
{
context.Backchannel.SetBasicAuthenticationOAuth(context.TokenEndpointRequest.ClientId, context.TokenEndpointRequest.ClientSecret);
return Task.CompletedTask;
};
I got this mostly out of the comments to your question and with the method mentioned there being marked as deprecated with a hint to the new extension method I use.
In 2022 you still can use the IdentityModel nuget.
It has some System.Net.Http extension methods.
https://github.com/IdentityModel/IdentityModel/blob/6f9e050167846724828138ba6ee8b626eb31c669/src/Client/BasicAuthenticationOAuthHeaderValue.cs
Keep in mind you have to remove the client_id/client_secret from the TokenEndpointRequest object.

Implement Google as a identity provider(IDP) using Kentor Auth Service Library in MVC Application?

Hi i am using kentor auth services(The Kentor Authentication services is a library that adds SAML2P support to ASP.NET and IIS web sites, allowing the web site to act as a SAML2 Service Provider (SP) ).Right now i am using Google as a Identity Privider for testing my application (Authentication using owin midddleware).I have set Up Google Identity provider also.But When i run the application it gives me an error
"400. That’s an error.
Invalid Request, invalid idpId in request URL, check if SSO URL is configured properly on SP side. That’s all we know."
i have used SingleSignOnServiceUrl=https://accounts.google.com/o/saml2/idp?idpid=xxxxxxxxx
DiscoveryServiceUrl=https://accounts.google.com/o/saml2/idp?idpid=xxxxxxxxx
Is that above configuration is correct?
I have attached App_start configuration below.This from Kentor auth services library.
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseKentorAuthServicesAuthentication(CreateAuthServicesOptions());
}
private static KentorAuthServicesAuthenticationOptions CreateAuthServicesOptions()
{
var spOptions = CreateSPOptions();
var authServicesOptions = new KentorAuthServicesAuthenticationOptions(false)
{
SPOptions = spOptions
};
var idp = new IdentityProvider(new EntityId("~/App_Data/GoogleIDPMetadata.xml"), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
Binding = Saml2BindingType.HttpRedirect,
SingleSignOnServiceUrl = new Uri("https://accounts.google.com/o/saml2/idp?idpid=xxxxxxxxx")
};
idp.SigningKeys.AddConfiguredKey(
new X509Certificate2(
HostingEnvironment.MapPath(
"~/App_Data/Kentor.AuthServices.StubIdp.cer")));
authServicesOptions.IdentityProviders.Add(idp);
// It's enough to just create the federation and associate it
// with the options. The federation will load the metadata and
// update the options with any identity providers found.
new Federation("http://example.com/Federation", true, authServicesOptions);
return authServicesOptions;
}
private static SPOptions CreateSPOptions()
{
var swedish = CultureInfo.GetCultureInfo("sv-se");
var organization = new Organization();
organization.Names.Add(new LocalizedName("Kentor", swedish));
organization.DisplayNames.Add(new LocalizedName("Kentor IT AB", swedish));
organization.Urls.Add(new LocalizedUri(new Uri("http://www.kentor.se"), swedish));
var spOptions = new SPOptions
{
EntityId = new EntityId("https://example.com/AuthServices"),
ReturnUrl = new Uri("https://example.com/Account/ExternalLoginCallback"),
DiscoveryServiceUrl = new Uri(https://accounts.google.com/o/saml2/idp?idpid=xxxxxxxxx"),
Organization = organization
};
var techContact = new ContactPerson
{
Type = ContactType.Technical
};
techContact.EmailAddresses.Add("authservices#example.com");
spOptions.Contacts.Add(techContact);
var supportContact = new ContactPerson
{
Type = ContactType.Support
};
supportContact.EmailAddresses.Add("support#example.com");
spOptions.Contacts.Add(supportContact);
var attributeConsumingService = new AttributeConsumingService("AuthServices")
{
IsDefault = true,
};
attributeConsumingService.RequestedAttributes.Add(
new RequestedAttribute("urn:someName")
{
FriendlyName = "Some Name",
IsRequired = true,
NameFormat = RequestedAttribute.AttributeNameFormatUri
});
attributeConsumingService.RequestedAttributes.Add(
new RequestedAttribute("Minimal"));
spOptions.AttributeConsumingServices.Add(attributeConsumingService);
spOptions.ServiceCertificates.Add(new X509Certificate2(
AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "/App_Data/Kentor.AuthServices.Tests.pfx"));
return spOptions;
}
Why i am getting 400 error when i redirect to google saml page? Thanks in advance
AFAIK Google offers no discovery service. Remove the DiscoveryServiceUrl from the configuration.
Also you should really clean up the configuration and not use the sample application's config.
For testing you can also use the Stub idp that is included in the project at which is available at http://stubidp.kentor.se

WebApi2 Google OAuth2 middleware error response

For user authentication with external providers such as Google, it is using specific Owin middlewares. As for example Microsoft.Owin.Security.Google. WebAPI2 template uses this to support implicit flow authentication (response_type=token). But what about Code flow?
Is it possible to implement Code flow (response_type=code)?
After debugging those OAuth providers I noticed that passing return_type=code to Google, it successfully authenticates and returns json with access and refresh tokens, then user gets signed in by api/Account/ExternalLogin endpoint but at the end of the flow I get redirected to
http://localhost:50321/?error=unsupported_response_type#.
I could not really find the flow where and why it is setting this specific error in the assembly.
Startup.Auth.cs looks like this:
public void ConfigureAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "self";
var tokenTimeSpanInHours = ConfigurationManager.AppSettings["AccessTokenLifeTimeInHours"];
OAuthServerOptions = new OAuthAuthorizationServerOptions
{
Provider = new ApplicationOAuthProvider(PublicClientId),
TokenEndpointPath = new PathString("/api/token"),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(Convert.ToInt16(tokenTimeSpanInHours)),
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthServerOptions);
var googleOAuthOptions = new GoogleOAuth2AuthenticationOptions
{
AccessType = "offline",
Provider = new CustomGoogleAuthProvider(),
ClientId = ConfigurationManager.AppSettings["GoogleAccountClientId"].ToString(),
ClientSecret = ConfigurationManager.AppSettings["GoogleAccountClientSecret"].ToString()
};
googleOAuthOptions.Scope.Add("profile");
googleOAuthOptions.Scope.Add("email");
googleOAuthOptions.Scope.Add("https://www.googleapis.com/auth/gmail.send");
app.UseGoogleAuthentication(googleOAuthOptions);
}
Where is the problem then? Do I need some explicit configuration to tell that I want code flow? Is it supported?

Resources