In my hub, I have something like this (pseudocode):
public override Task OnConnected()
{
var isLegal = Authorize();
if (!isLegal) //not authorized
{
//how do I deny him this connection?
}
return base.OnConnected();
}
How to disconnect/deny connection to a user?
Add authorize attribute over the class,the user will be denied, if not authenticated.
The following link explains how to do Hub authentication and authorization.
http://www.asp.net/signalr/overview/security
If you choose not to use the existing auth mechanism, you'll need to check if the user is authorized in every hub method and return appropriate messages.
Related
I am integrating with WebSockets in my Spring MVC application. The authentication mechanism for my application is OAuth.
I was able to pass my OAuth token in connection string when connecting to SockJS:
var webSocketUrl = '/websocket' + '?access_token=' + auth.access_token;
var socket = new SockJS(webSocketUrl);
var stompClient = Stomp.over(socket);
Now I can send messages and subscribe to STOMP channels:
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/greetings', function(greeting){
console.log(greeting);
});
stompClient.send("/app/hello", {}, JSON.stringify('John'));
});
In my backend I am able to get user principle injected to my STOMP controller methods (which means that Spring understands that there is an OAuth token in connection string):
#Controller
public class MyWebsocketsController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public String greet(String name, Principal authorizedUser) {
return "Hello, " + name + ", you have authorized as " + authorizedUser.getName();
}
}
Now I would like to require user authorization on all messages and subscriptions, i.e. I would like to make sure that all calls to web sockets return 403 error code if no valid token was provided when connecting to SockJS.
I add this security configuration to my project:
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, HEARTBEAT, UNSUBSCRIBE, DISCONNECT).permitAll()
.simpDestMatchers("/app/**").authenticated()
.simpSubscribeDestMatchers("/topic/**").authenticated()
.simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
.anyMessage().denyAll();
}
}
But it does not seem to do the job. If I remove access token from connection string, I am still able to send messages to controller and to subscribe to channel.
// This stil works:
var webSocketUrl = '/websocket'; // + '?access_token=' + auth.access_token;
Of course, now I can't get the user principle in my controller, but except for this web sockets work fine.
I would appreciate any ideas how to make this thing work or any explanation why web sockets security configuration is not working for my case. What am I missing? Thanks.
Setup
Client: mobile app built on Cordova
Backend: ASP.net WebAPI2 (based on the standard template) configured with facebook login provider.
Problem
Having authenticated the user in the mobile app and received a Facebook access token, I pass this in subsequent requests as an HTTP header ("Authorization: Bearer "). This returns status 401 Unauthorized.
Questions
What am i missing here? How can i access the WebAPI controller actions based on the Facebook access token obtained on the mobile device?
On a high level, what i'm trying to achieve is this:
User opens mobile app and authenticates with Facebook
If user is not registered as a local user, he must choose a username to complete the registration
User is registered and can access API
I was facing the same problem and I found a really good solution here: http://codetrixstudio.com/mvc-web-api-facebook-sdk/
The WebApi web site can't understand the access token provided by Facebook. I guess it's because it hasn't been issued by itself (LOCAL AUTHORITY) but by an external provider. The approach explained in the link above is based on validating the token given by Facebook using it's API and recreating the access token.
So, you'll need some additional steps to achieve your goal.
The external providers have API so you can get information. For example, the https://graph.facebook.com/me?access_token={0} can be used to check if the token is valid. On the server side, you'll need to make a https web request to this URL passing the token (and the secret app as a proof, if the app is configured to ask it in Facebook).
Given the token is ok, you'll create an identity (ClaimsIdentity) using the information you've got at the API (id and username, for example). This identity will be needed to make an instance of the AuthenticationTicket class so you'll be able to issue a new access token for your Cordova app. Use this new bearer access token in the Authorization header of your https calls and your WebApi will recognized it as valid calls.
Ps. The good thing here is that you can set the token's expiration.
Since API's are stateless, there are multiple ways to secure it. In this case the mobile app has authenticated the user, but the API has not.
You can register the user's email and facebook ID into the database using a anonymous route. this can serve as both the login and register technically. (you could secure it with a clientid via OAuth if you don't want it fully open) along with thier current token. You verify the user against the facebook API on the server before registering of course just in case.
Create a custom route handler to secure the account controller or any other routes. The custom route handler would check the database for the current FB token and fb ID combo as well as token expire time, since you don't want to keep authenticating if it's expired.
Facebook has two types of tokens.
A Short lived token, initially created when you login and a long term token that lasts up to 60 days vs 1-2 hours.
for the api side, i suggest sticking to the short lived token and re-authenticate once expired but thats up to you.
You should grab a facebook SDK of your choice to verify and pull account info.
Example Secured Route:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "routename",
routeTemplate: "account",
constraints: null,
handler: new CustomFBHandler()
{
InnerHandler = new HttpControllerDispatcher(config)
},
defaults: new {controller = "default"}
);
}
Custom Handler: (note that you should pass any needed dependancies in the construtor)
public class CustomHandler : DelegatingHandler
{
public CustomHandler()
{
}
protected async Task<bool> IsAuthenticated(HttpRequestMessage requestMessage)
{
//Authenticate FB User Info HERE Against the Registered/logged in user....
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
bool isAuthenticated = false;
try
{
isAuthenticated = await IsAuthenticated(request);
}
catch (Exception e)
{
var response = request
.CreateResponse(HttpStatusCode.InternalServerError, new { status = new { code = 333, error = true, message = e.Message } }, new JsonMediaTypeFormatter());
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(Configuration.AuthenticationScheme));
return response;
}
if (!isAuthenticated)
{
var response = request
.CreateResponse(HttpStatusCode.Unauthorized,new {status=new{code=1,error=true,message="Authorization Failed"}},new JsonMediaTypeFormatter());
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(Configuration.AuthenticationScheme));
return response;
}
return await base.SendAsync(request, cancellationToken);
}
}
You should send the FB Token and Facebook user id in the Headers. Once authenticated you can use the token/id to pull the user info you need from the database.
What I want:
A token generator use OAuthAuthorizationServer and token consumer use OAuthBearerAuthentication (authenticate the access token).
Use OWIN pipeline to manage all stuff, token stuff and web api stuff.
What about the code:
public void Configuration(IAppBuilder app)
{
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = "/Authorize",
AllowInsecureHttp = true,
Provider = new OAuthAuthorizationServerProvider
{
OnGrantCustomExtension = GrantCustomExtension,
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
}
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider
{
//Handles applying the authentication challenge to the response message.
ApplyChallenge=MyApplyChallenge,
//Handles processing OAuth bearer token.
RequestToken=MyRequestToken,
//Handles validating the identity produced from an OAuth bearer token.
ValidateIdentity = MyValidateIdentity,
}
});
app.UseWebApi(new WebApplication3.Config.MyWebApiConfiguration());
}
What's the question:
The 3 properties of OAuthBearerAuthenticationProvider,
ApplyChallenge, RequestToken and ValidateIdentity. How to
implement the 3 methods?
In the token authetication process, What I thought is to decrypt the access token, validate the token from the client, and if the token is validated, put the identities of the token to the HttpContext.Current.User.
The OAuthBearerAuthenticationProvider's responsibility is to fulfill the
previous steps. Am I right?
As you know, UseOAuthAuthorizationServer has the job of authenticating the user. Then, UseOAuthBearerAuthentication has the job of ensuring that only authenticated users can access your application. Often, these two jobs are assigned to different web application. It looks like your application is doing both.
There are certainly some cases were you need to override the default OAuthBearerAuthenticationProvider. Maybe you do, or maybe you don't In my case, ApplicationCookie didn't quite fit the scenario. So, I'm storing a 3rd party JWT token in a cookie, rather than the header, and using it to indicate that the user is authenticated to a web application. I also needed to redirect to my own login page, rather than provide a 401.
Here's an implementation that does both:
public class CustomOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
public Task ApplyChallenge(OAuthChallengeContext context)
{
context.Response.Redirect("/Account/Login");
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
string token = context.Request.Cookies[SessionKey];
if (!string.IsNullOrEmpty(token))
{
context.Token = token;
}
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
return Task.FromResult<object>(null);
}
}
I didn't need to do anything special in ValidateIdentity, but I needed to satisfy the interface.
To wire this up, tell your app to use JwtBearerAuthentication with your provider:
// controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AllowedAudiences = audiences.ToArray(),
IssuerSecurityTokenProviders = providers.ToArray(),
Provider = new CookieOAuthBearerProvider()
}
);
I'm trying to create a simple user authentication function but I just can't get it to work.
Here is the code I'm working on:
public class LoginController : ApiController
{
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
public bool Login(string token)
{
//Check token
if (.....)
{
//Authenticate user
var identity = new GenericIdentity("Test user");
SetPrincipal(new GenericPrincipal(identity, new string[]{"Test role"}));
}
}
[Authorize]
public string TestFun()
{
return "Hello " + User.Identity.Name;
}
}
So, if I try to call method TestFun() first, it returns error code 401 like it should.
However when I call method Login() it should somehow save user credentials, but this is where I get lost, I just can't get it to work.
TestFun() always returns error code 401 even if I call Login() first.
If I try to put return "Hello " + User.Identity.Name; in the Login() function it returns correct username, but in the TestFun() the user is not available.
I've even tried using Sessions and FormsAuthentication but I just can't get it to work, even on this really simple example.
Can someone please tell me what am I missing?
Thanks!
The Login method sets the principal for current request only. Just after the request completes, the principal context is wiped out so that the server can handle other requests for other users. When a new request comes, eons later from the server perspective, the principal context no longer exists and if nothing restores it, the request is unauthenticated.
To fix this you have to return something from your login method to the client. Not only bool but rather - an authentication token. Something the client could use to authenticate further requests.
It could be anything. Forms cookie would be fine as long as the client remembers to append it to further requests. Another common practice is to have a custom authentication token returned to the client and then appended by the client in a custom authentication header. And as forms cookies are handled by the Forms Authentication module, custom headers would need a custom mvc authentication filter or custom asp.net authentication module so that the token is readed, the identity is extracted and restored just before the request is about to execute.
If you don't like to bake your own token infrastructure, I would also recommend OAuth2 tokens. There is a great book that contains easy to follow examples on this and other possible authentication methods:
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822/ref=sr_1_1?ie=UTF8&sr=8-1&keywords=web+api+security
I just got the same issue, yes, I agreed we need to save that principal into somewhere (cookie, session) for other action to use, so, in SetPrincipal function I added
HttpContext.Current.Session["user"] = HttpContext.Current.User;
Now, the issue is how to get it back for other action, the idea popups in my mind is to extend AuthorizeAttribute and override IsAuthrized function, it will read the session first and if it found the session, it will return true, otherwise it will return false.
namespace BinZ
{
public class MyAuthorizeAttribute:AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext) {
HttpContext.Current.User = HttpContext.Current.Session["user"] as IPrincipal;
return HttpContext.Current.User != null;
}
}
}
Please remember to replace [Authorize] to [MyAuthorizeAttribute] in WebApi controller.
It works for me very well.
Cheers
for example I have a web API : http://example.com/api/product.
I have a C# client to consume this web API. Something like that to get whole list of product.
// List all products.
HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
// Parse the response body. Blocking!
var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result;
foreach (var p in products)
{
Console.WriteLine("{0}\t{1};\t{2}", p.Name, p.Price, p.Category);
}
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}
How do I pass the username and password from C# client to server's API? What I want is when the C# client to get whole product list from web API.
The client will send the username and password to the server's API. if the server's web API checks whether it is authorized user from database, if not don't let it get product list.
I used the following approach in a proof of concept some time ago, I hope it helps you.
I wrote something like this, an "AuthenticationController" with 2 methods:
public bool Login(string username, string password, bool rememberMe)
{
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, rememberMe);
return true;
}
return false;
}
public void Logout()
{
FormsAuthentication.SignOut();
}
The Login method creates a cookie that will be sent to the client; then, in each request, you need to send it back to the server. You can use the [Authorize] attribute in your controller actions to validate allowed roles and rights.
My recommendation is to use have an authentication routine that will assign a token to the client. The client would then cache that token and pass that token in subsequent requests. The authentication routine should be via SSL to prevent sniffing on the wire and shouldn't be stored on the device at all (the token can be cached to the device).
This will give you a fair bit of control over the client. Your service is then in a position where it can preemptively deactivate the client (kill the token and force a re-auth - essentially a timemout situation). You are also in a position to protect your application on the client (if the application is compromised on the device the user credentials won't be passed around).
You could use DotNetOpenAuth to get you started along this path.
[System.Web.Mvc.AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string loginIdentifier)
{
if (!Identifier.IsValid(loginIdentifier))
{
ModelState.AddModelError("loginIdentifier",
"The specified login identifier is invalid");
return View();
}
else
{
var openid = new OpenIdRelyingParty();
IAuthenticationRequest request = openid.CreateRequest(
Identifier.Parse(loginIdentifier));
// Require some additional data
request.AddExtension(new ClaimsRequest
{
BirthDate = DemandLevel.NoRequest,
Email = DemandLevel.Require,
FullName = DemandLevel.Require
});
return request.RedirectingResponse.AsActionResult();
}
}
Source: Sample Code