How to add third party authentication urls to ASP.Net MVC - oauth

ASP.Net projects come with builtin support for loginprovider additions.
Eg: for facebook enabling we go App_start->Startup.Auth.cs and uncomment this line, simple!
app.UseFacebookAuthentication(
appId: "",
appSecret: "");
But Facebook has apis like this https://graph.facebook.com/oauth/authorize
and google has "https://www.googleapis.com/"
and another third party has this "https://www.wunderlist.com/oauth/authorize"
but I did not see this urls in the asp.net project. How is .Net accessing these URl? what if the url keep changing? How can I add a new third party provider to my project?

The providers for Facebook/Twitter/etc use custom libraries from the Owin project, you can see the source code for it all there. The endpoints are very unlikely to change as it would break countless websites.
The URLs are hardcoded in the libraries, for example the Facebook library has this:
namespace Microsoft.Owin.Security.Facebook
{
internal static class Constants
{
public const string DefaultAuthenticationType = "Facebook";
internal const string AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
internal const string TokenEndpoint = "https://graph.facebook.com/oauth/access_token";
internal const string UserInformationEndpoint = "https://graph.facebook.com/me";
}
}

Related

Sustainsys SAML2 Sample for ASP.NET Core WebAPI without Identity

Does anyone have a working sample for Sustainsys Saml2 library for ASP.NET Core WebAPI only project (no Mvc) and what's more important without ASP Identity? The sample provided on github strongly relies on MVC and SignInManager which I do not need nor want to use.
I added Saml2 authentication and at first it worked fine with my IdP (I also checked the StubIdP provided by Sustainsys) for first few steps so:
IdP metadata get properly loaded
My API properly redirects to sign-in page
Sign-in page redirects to /Saml2/Acs page, and I see in the logs that it parses the result successfully
However I don't know how to move forward from there and extract user login and additional claims (my IdP provided also an e-mail, and it is included in SAML response which I confirmed in the logs).
Following some samples found on the web and modyfing a little bit the MVC Sample from GitHub I did the following:
In Startup.cs:
...
.AddSaml2(Saml2Defaults.Scheme,
options =>
{
options.SPOptions.EntityId = new EntityId("...");
options.SPOptions.ServiceCertificates.Add(...));
options.SPOptions.Logger = new SerilogSaml2Adapter();
options.SPOptions.ReturnUrl = new Uri(Culture.Invariant($"https://localhost:44364/Account/Callback?returnUrl=%2F"));
var idp =
new IdentityProvider(new EntityId("..."), options.SPOptions)
{
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = true, // At first /Saml2/Acs page throwed an exception that response was unsolicited so I set it to true
MetadataLocation = "...",
SingleSignOnServiceUrl = new Uri("...") // I need to set it explicitly because my IdP returns different url in the metadata
};
options.IdentityProviders.Add(idp);
});
In AccountContoller.cs (I tried to follow a somewhat similar situation described at how to implement google login in .net core without an entityframework provider):
[Route("[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
private readonly ILog _log;
public AccountController(ILog log)
{
_log = log;
}
[HttpGet("Login")]
[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
return new ChallengeResult(
Saml2Defaults.Scheme,
new AuthenticationProperties
{
// It looks like this parameter is ignored, so I set ReturnUrl in Startup.cs
RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl })
});
}
[HttpGet("Callback")]
[AllowAnonymous]
public async Task<IActionResult> LoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync(Constants.Auth.Schema.External);
_log.Information("Authenticate result: {#authenticateResult}", authenticateResult);
// I get false here and no information on claims etc.
if (!authenticateResult.Succeeded)
{
return Unauthorized();
}
// HttpContext.User does not contain any data either
// code below is not executed
var claimsIdentity = new ClaimsIdentity(Constants.Auth.Schema.Application);
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
_log.Information("Logged in user with following claims: {#Claims}", authenticateResult.Principal.Claims);
await HttpContext.SignInAsync(Constants.Auth.Schema.Application, new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
}
TLDR: Configuration for SAML in my ASP.NET Core WebApi project looks fine, and I get success response with proper claims which I checked in the logs. I do not know how to extract this data (either return url is wrong or my callback method should work differently). Also, it is puzzling why successfuly redirect from SSO Sign-In page is treated as "unsolicited", maybe this is the problem?
Thanks for any assistance
For anyone who still needs assistance on this issue, I pushed a full working example to github which uses a .Net Core WebAPI for backend and an Angular client using the WebAPI. you can find the example from here:
https://github.com/hmacat/Saml2WebAPIAndAngularSpaExample
As it turned out, the various errors I've been getting were due to my solution being hosted inside docker container. This caused a little malfunction in internal aspnet keychain. More details can be found here (docker is mentioned almost at the end of the article):
https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?tabs=aspnetcore2x&view=aspnetcore-2.2
Long story short, for the code to be working I had to add only these lines:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/some/volume/outside/docker")); // it needs to be outside container, even better if it's in redis or other common resource
It fixed everything, which includes:
Sign-in action to external cookie
Unsolicited SSO calls
Exceptions with data protection key chain
So it was very difficult to find, since exceptions thrown by the code didn't point out what's going on (and the unsolicited SSO calls made me think that the SSO provider was wrongly configured). It was only when I disassembled the Saml2 package and tried various code pieces one by one I finally encoutered proper exception (about the key chain) which in turned led me to an article about aspnet data protection.
I provide this answer so that maybe it will help someone, and I added docker tag for proper audience.

Get Error (error=access_denied) while logging in MVC5 application with facebook SDK

I have developed an application with ASP.NET MVC5. I have used Facebook external authentication in my application.
When I debug this application with the "Locallhost" domain, the Facebook login works well but when I publish the application in the main server,the AuthenticationManager.GetExternalLoginInfo() returns null and it gives me an error like this in the url:
http://xxxxx.com/Account/ExternalLoginCallback?ReturnUrl=%2Fen&error=access_denied#_=_
I have set the "Site URL" as "http://xxxx.com" and "Valid OAuth redirect URIs" as "http://xxxx.com/signin-facebook" in the Facebook development console.
My setting in the Startup.Outh.cs file is:
var FacebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions();
FacebookOptions.AppId = ConfigurationManager.AppSettings["Facebook_User_Key"];
FacebookOptions.AppSecret = ConfigurationManager.AppSettings["Facebook_Secret_Key"];
FacebookOptions.Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
}
};
FacebookOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseFacebookAuthentication(FacebookOptions);
I don't know why the external login does not work only in the server with my main domain name. please help me about this problem.
I encountered pretty much the same symptoms you describe:
shortly:
A Facebook authentication worked well on localhost, and after uploading the project to another server (and changing the site URL on Facebook console), authentication did not succeed.
I would recommend you roll back to the MVC template code, and if that works - notice any changes you have made to the middleware code (Startup.Auth.sc).
In particular pay attention to code that interacts with LOCAL configuration, such as Disk I/O and OS permissions for local services.
My particular case:
Starting from the Owin/Katana supported Visual Studio template of a WebAPI project, external login was working perfectly with Facebook, Microsoft and Google OAuth middleware, when testing on localhost.
Later I added come code to Startup.Auth.sc because I needed further authentication activity.
So this was the original code:
public void ConfigureAuth(IAppBuilder app)
{
// see WebAPI template of Visual Studio 2013/2015
...
app.UseFacebookAuthentication(
appId: 99999999,
appSecret: *******);
}
and this was replacement:
public void ConfigureAuth(IAppBuilder app)
{
// see WebAPI template of Visual Studio 2013/2015
...
app.UseFacebookAuthentication(GetFacebookAuth());
}
private FacebookAuthenticationOptions GetFacebookAuth()
{
string picRequest =
String.Format("/me/picture?redirect=false&width={0}&height={0}", ProfileInfoClaimsModel.PIC_SIDE_PX);
var facebookProvider = new FacebookAuthenticationProvider()
{
OnAuthenticated = async (context) =>
{
var client = new FacebookClient(context.AccessToken);
dynamic me = client.Get("/me?fields=id,name,locale");
dynamic mePicture = client.Get(picRequest);
// storing temporary social profile info TO A LOCAL FOLDER
// uploading the local folder to a service WITH A LOCAL CREDENTIAL FILE
...
}
};
var options = new FacebookAuthenticationOptions()
{
AppId = 0123456789,
AppSecret = ******,
Provider = facebookProvider,
};
return options;
}
You may notice that my comments will make the problem obvious - the code points to local resources.
Then I published the project to a virtual server (by Amazon EC2) running Windows Server 2012 with IIS 8.5.
From that moment I kept getting error=access_denied in the redirect from /signin-facebook.
I decided to follow this good old concept, and go back to the original template code. Pretty soon I figured out that I forgot to configure the new server. For instance, the folder the code refers to did not exist and the site had no permission to create it.
Obviously, that solved it.

Apache Oltu Github integration example with Spring MVC

I'm developing an "Apache Oltu Spring MVC Github" integration example. In this example I will be sending "App ID" and "Secret" to get the "access_token" in order to access the protected resources like "Gist", "user" etc.
So first step is to create / register the "App" using https://github.com/settings/applications/new.
Once you create a App make sure to Note: AppID and Secret, we need these values to be used in Spring code.
To develop this functionality / code - I search a lot and I did not find any ready made code. So I decided to furnish / explain my code below. So one can find these links useful.
I've taken a reference of following URL's to developed whole code:-
https://developer.github.com/v3/oauth/
https://cwiki.apache.org/confluence/display/OLTU/OAuth+2.0+Client+Quickstart
http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-apache-oltu.html
Attached is the screen shot to register the "App" on Github. "MyApp" is the App that I created.
Use the same code from http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-apache-oltu.html, just make sure to change the
AUTHORIZATION_URL = "https://github.com/login/oauth/authorize";
ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"
To get Protected Resource like User Profile use: https://api.github.com/user
The output I get when run the code:
The user4798111 has mentioned is correct and just adding some more details. Pre-requisite, you need to register App on Github. Once you registered the App, you will get the CLIENT_SECRET,CLIENT_ID to be used to get the Protected resources from the github.
If you're using the OAuthClientRequest API to to make an initial call, you need to have the following details
private static final String AUTHORIZATION_URL = "https://github.com/login/oauth/authorize";
private static final String CLIENT_ID = "8515a1e4XXXXXXX";
private static final String CLIENT_SECRET = "ae3487601d891d257f7193XXXXXXXXX";
private static final String REDIRECT_URL = "http://localhost:8080/apache-oltu/github/redirect";
private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
#RequestMapping(value = "/auth", method = RequestMethod.GET)
public String authenticate() throws OAuthSystemException {
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(AUTHORIZATION_URL)
.setClientId(CLIENT_ID)
.setRedirectURI(REDIRECT_URL)
.setResponseType("code")
.setScope("user,gist,user:email,user:follow,public_repo,repo,repo_deployment,repo:status,repo:invite")
.buildQueryMessage();
System.out.println("REDIRECT TO: "+request.getLocationUri());
return "redirect:" + request.getLocationUri();
}
The same something simllar you would need to use like below
request= new OAuthBearerClientRequest("https://api.github.com/user").
setAccessToken(oAuthResponse.getAccessToken()).
buildQueryMessage();
The information about the scopes and other details can be found here:
https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-authorization-options-for-oauth-apps/
https://developer.github.com/apps/building-integrations/setting-up-and-registering-oauth-apps/about-scopes-for-oauth-apps/
The result which could see is below for reference:
{
"login":"JavaHelper",
"id":8208031,
"avatar_url":"https://avatars0.githubusercontent.com/u/8208031?v=4",
"gravatar_id":"",
"url":"https://api.github.com/users/JavaHelper",
"html_url":"https://github.com/JavaHelper",
"followers_url":"https://api.github.com/users/JavaHelper/followers",
"following_url":"https://api.github.com/users/JavaHelper/following{/other_user}",
"gists_url":"https://api.github.com/users/JavaHelper/gists{/gist_id}",
"starred_url":"https://api.github.com/users/JavaHelper/starred{/owner}{/repo}",
"subscriptions_url":"https://api.github.com/users/JavaHelper/subscriptions",
"organizations_url":"https://api.github.com/users/JavaHelper/orgs",
"repos_url":"https://api.github.com/users/JavaHelper/repos",
"events_url":"https://api.github.com/users/JavaHelper/events{/privacy}",
"received_events_url":"https://api.github.com/users/JavaHelper/received_events",
"type":"User",
"site_admin":false,
"name":"JavaProgramer",
"company":null,
"blog":"",
"location":null,
"email":null,
"hireable":null,
"bio":null,
"public_repos":45,
"public_gists":0,
"followers":4,
"following":60,
"created_at":"2014-07-19T10:03:42Z",
"updated_at":"2017-09-09T12:55:57Z",
"private_gists":0,
"total_private_repos":0,
"owned_private_repos":0,
"disk_usage":142270,
"collaborators":0,
"two_factor_authentication":false,
"plan":{
"name":"free",
"space":976562499,
"collaborators":0,
"private_repos":0
}
}

asp.net identity 2.1 google authentication

I'm using trying to use Google authentication in an ASP.NET MVC application.
For testing purposes I'm using the template app generated by VS2013 Update 4
In Google settings the return URLs are properly set and Google+ API is turned on. The app works fine when I publish it to an azure website. I can login using Google accounts without any problems.
However I'd like to deploy it on premises but here we have a reverse proxy setup which works like this:
the server sees itself as server01.mysite.com but this is an
internal name
outside world sees it as www.mysite.com (certain paths are
reverese proxied to the server01.mysite.com
Essentially www.mysite.com/myapp is reverse proxied to server01.mysite.com/myapp
With this setup I can't seem to use Google authentication. GetExternalLoginInfoAsync returns null and the app redirects itself to the login page.
By default the system generates a redirectUri using the private hostname. I tried changing it to the public address but this does not solve the problem.
Below is what I did at startup.auth.cs
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "...",
ClientSecret = "...",
Provider = new GoogleOAuth2AuthenticationProvider
{
OnApplyRedirect = context =>
{
var redirectUri = context.RedirectUri.Replace("server01", "www");
context.Response.Redirect(redirectUri);
},
}
});
Is there anyway I can make Google authentication work in a setup like this?
Thanks
To achieve this one has to tell the app to use the outside URL earlier so that the relevant hashes are built taking that into account. So instead of changing the redirect URI at the OnApplyRedirect call this before UseGoogleAuthentication:
app.Use((context, next) =>
{
context.Request.Host = new HostString(
context.Request.Host.Value.Replace("server01", "www"));
return next();
}
);
and remove the Provider=... from UseGoogleAuthentication
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "...",
ClientSecret = "..."
});

How to use swagger with OAuth API?

Is it possible to use swagger as a documentation/testing tool for APIs that use OAuth2? I don't see anything on the swagger site (or anywhere else for that matter). Every usage I've seen uses either an API key, HTTP basic, or cookies.
I have been working along the same lines. Swagger will accept any header or URL defined api key or token. Adding a validation helper to the api and app is a standard approach.
Oauth does require a HTML review and or login to start the handshake aouth process. This means that a swagger api will need to support a web interface for a standard login and scope acceptance. Rolling oauth into swagger results in a few logic loops, which long term are not easy to support.
A different approach we are exploring is the option to let the api handle and store access tokens for a number of different oauth providers; GitHub, twitter and Facebook. This might result in login loops as well.
late to the party here but oAuth support is now in 1.3.0-RC1 of swagger-core. The javascript library which can support oAuth was released yesterday in swagger-js. Finally, the swagger-ui is in develop phase, and will soon have a oAuth implicit and server flow.
the blog´s post http://developers-blog.helloreverb.com/enabling-oauth-with-swagger/ cited by #fehguy shows an example of java code to include the authorization data in json generated by swagger, however my question was where it should be included with app with Spring, JAXRS and CXF. I didn´t find it in CXF + JAXRS Sample :https://github.com/swagger-api/swagger-core/tree/master/samples/java-jaxrs-cxf
However, looking for a bit more and gotcha !
https://github.com/swagger-api/swagger-core/blob/master/samples/java-jersey-spring/src/main/resources/beans-asset-ws.xml
Is necessary include a Bean with a class called Bootstrap (extends HttpServlet) and a static block !
Opinion: Maybe it would be more “spring-friendly” loaded from annotations by SwaggerConfig Scanner in Rest class instead a static block in a servlet.
#Configuration
public class SwaggerConfiguration {
#Bean
#DependsOn("jaxRsServer") //org.apache.cxf.endpoint.Server bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
servletContext.setAttribute("swagger", swagger);
}
};
}
#Bean
public Feature swaggerFeature() {
XSwagger2Feature feature = new XSwagger2Feature();
return feature;
}
#Bean
public FilterRegistrationBean swaggerApiFilter() {
ApiOriginFilter filter = new ApiOriginFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
public static class XSwagger2Feature extends Swagger2Feature {
#Override
protected void addSwaggerResource(Server server) {
super.addSwaggerResource(server);
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
swagger.securityDefinition("api_key", new ApiKeyAuthDefinition("api_key", In.HEADER));
swagger.securityDefinition("petstore_auth",
new OAuth2Definition()
.implicit("http://petstore.swagger.io/api/oauth/dialog")
.scope("read:pets", "read your pets")
.scope("write:pets", "modify pets in your account"));
}
}
}
IOdocs from mashery seems to support OAuth, but it's quite different from swagger (redis, node, etc.). It's available on github.

Resources