So I need to validate oauth token on every page except for site/login, site/logout, site/error, site/auth. Building off of the advanced template, this would obviously be in the backend.
What would be the proper way of doing this in Yii2?
extending all controllers from some sort of base controller?
bootstrapping a class in config?
custom filter?
behaviours?
Essentially I just need a function to run on every page except the 4 mentioned above.
Yii 2.0 already have 3 authentication methods implemented as filters :
yii\filters\auth\HttpBasicAuth
yii\filters\auth\HttpBearerAuth
yii\filters\auth\QueryParamAuth
Plus yii\filters\auth\CompositeAuth to use more than one at the same time. They are usually attached to each controller within a behavior :
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => CompositeAuth::className(),
'authMethods' => [
HttpBasicAuth::className(),
HttpBearerAuth::className(),
QueryParamAuth::className(),
],
];
return $behaviors;
}
And all of them have an $except and $only properties to choose to which actions you are applying them. So you may have something like this in your SiteController :
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['login','logout','error','auth']
];
return $behaviors;
}
And you may have the the same behavior but without the except property in all the other controllers. Or you can make all the other controllers extends a common controller where that authenticator behavior is implemented.
Those filters will use the built-in User class (as set in your config file) which implements the IdentityInterface to authenticate a user. That interface has already a findIdentityByAccessToken() method that you can use to validate a token instead of using findIdentity() to register a logged in user and make it accessible within Yii::$app->user->identity or Yii::$app->user->id.
What I'm trying to explain here is kind of a summary of how Authentication is implemented within the built-in Yii RESTful API framework which may be better explained here :
http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html
And which I consider a good exemple to follow. There is also this tutorial that describes authentication by access token and how it is implemented within the User class. It is about REST but the technique should be the same for a non REST app too as both are using the User class.
Related
I have set up an API in ASPNET with .NET 6, where I am registering two schemes in my autentication service.
builder.Services.AddAuthentication("MyDefaultScheme")
.AddJwtBearer("MyDefaultScheme", options => { ... })
.AddJwtBearer("SomeSecondaryScheme", options => { ... });
builder.Services.AddAuthorization();
These are two different authorities that both issues valid JWT tokens.
In my API controllers, I have set up different controllers which responds to these two schemes, using the "AuthenticationSchemes" property in the [Authorize] attribute. But I also have a controller that accepts tokens from both schemes.
[ApiController]
[Authorize(AuthenticationSchemes = "MyDefaultScheme,SomeSecondaryScheme")]
public class SomeApiController : ControllerBase
{
...
}
This works good, but my question is:
Is there a way to find which auth scheme the token comes from, in the request context or a helper?
The reason for this is that the data I'm looking for may exist under different scope names depending on whether the token is issued by "MyDefaultScheme" or "SomeSecondaryScheme". Like username that may exist in the "sub" claim in one scheme and "preferred_username" in the other. Or that roles are named differently, but handled the same.
In that case, it would be nice to know which scheme it came from and do a switch based on that. I know I can get the "iss" claim value and then parse IAuthenticationHandlerProvider from GetRequiredService, but I'm looking for a simpler solution, if any.
Thanks!
builder.Services.AddAuthorization("MyDefaultScheme");
pretty new to creating Web APIs, I am currently trying to secure my API and have a couple of questions
So basically I have a Web API and an MVC app. The API currently has a controller called Account that has two methods Register and Login. The MVC app has the same controller with actions but just calls the api methods.
Now basically they way I see it, I only ever want my MVC app to use the Web API, so ill have an api key in the MVC app webconfig that gets passed to the API each time? Also users need to authenticate so at the same time passing the user details?
Will this mean I need to setup two AuthAttributes? One for a user and one for the api details?
EDIT
To take this example a bit further and to demonstrate what I need
I have an WebUI that has a controller called CreateTeam. This passes a Team model up to the api Controller method CreateTeam, the api method requires that the user is authorized to create a team. Now this works fine but....
I also have a controller on my api called LeaguesController, which has a method AddNewTeamsToLeagues. Now I have a console app that runs every hour that calls this method on the api to add new teams to leagues. Now I dont ever want anyone to call this method on the api, I only ever want the console app to be able to use it. Whats the best way to secure this?
One solution is to use the token generated by [AntiForgeryValidation] (the Razor helper is #Html.AntiForgeryToken). You can use the following token (generated on your MVC View) to assist with validation if you'd like (it can be very helpful) or use your own:
<input name="__RequestVerificationToken" type="hidden" value="some-generated-value" />
If you're using jQuery you can override the default Ajax options (jQuery.ajaxSetup() - API documentation) to automatically add this to your request headers and validate against it (in whatever implementation you want). You can also obviously send in a username and whatever else you'd like for verification uses.
Then, you can have your Web API have a filter that validates against this information (and whatever else you'd like) using AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);.
Unfortunately, until MVC6/Web API3 the two controller types have completely different implementation so you may have to write a customer filter yourself to handle authentication. There are dedicated [Authorize] attributes for both MVC and Web API but they have have different namespaces (System.Web.Http.AuthorizeAttribute vs System.Web.Mvc.AuthorizeAttribute).
Hope this helps. For further reading, you can check out this blog post.
-- Edit to reply to your updated comment about your Console application --
You could always create a Filter that only allows local connections, specific IP addresses, certain LDAP/AD Security Groups, etc to have access to a specific controller/controller action. In your case of a Console application you would need to decide how you'd want that to be secured; once you decide to you can go from there. So, say you want to allow only specific members of an AD Security Group to access the controller, you could throw together a filter like so:
namespace YourAppName.Filters
{
public class AuthorizeADAttribute : AuthorizeAttribute
{
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (base.AuthorizeCore(httpContext))
{
var groups = Groups.Split(',').ToList();
var context = new PrincipalContext(ContextType.Domain, "YourDomainHere");
var userPrincipal = UserPrincipal.FindByIdentity(
context,
IdentityType.SamAccountName,
httpContext.User.Identity.Name);
foreach (var group in groups)
if (userPrincipal.IsMemberOf(context,
IdentityType.Name,
group))
return true;
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var result = new ViewResult();
result.ViewName = "NotAuthorized";
result.MasterName = "_Layout";
filterContext.Result = result;
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
}
And then apply it to a controller or a method inside of your controller, like so:
[AuthorizeAD(Groups = "SecurityGroupToAuth, League Admins, Console App Group")]
public YourViewModelHere AddNewTeamsToLeagues()
{
// do stuff
}
So, to answer your initial question: you'll likely need two different attributes/filters for the different types (between handling the AntiforgeryToken and then the console app). Unfortunately without knowing how your application and console application are hosted (different machines, same subnet, on the same network, etc) I can't give much more information but hopefully this helps point you in the right direction for creating your own filter/attribute(s).
It seems to me that somewhere a rabbit is being pulled out of a hat when it comes to DI in Web API Controllers.
I grok that:
0) The Controller in a Web API project can be called with various classes to be instantiated, all of which implement the interface on which the Controller depends. e.g., with this Controller code:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
..."deptsRepository" can be a class that implements IDepartmentRepository and retrieves test data, OR it can be a class that implements IDepartmentRepository and retrieves production data, OR (etc.)
1) Web API decides which Controller is called based on the URI the client calls, and that Web API decides which method in that Controller is called based on what type (GET, POST) etc. it is and what args, if any, are passed with it.
2) Castle Windsor intercepts this automatic control of Controllers with its own replacement routing engine.
What I don't grok is just where the developer injects the concrete class that implements the interface expected by the Controller. IOW, if I want to run the class that pulls from test data, where do I add code to specify that? I would think it would be somewhere in Global.asax.cs, something like (pseudocode):
// Use test data for now
DeptsControllerClass = TestDataClass;
//DeptsControllerClass = ProductionDataClass;
IOW, where does one specify, "This time, I want you to inject THIS concrete class which implements the required interface"?
As you wrote at point 1, Routing and IoC are two different things.
Once the routing engine figures out which controller has to be invoked, a "controller factory" will be invoked.
WebApi framework allows to plug your own factory as following:
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new WindsorCompositionRoot(this.container));
Read more on Mark Seemann post about webapi&windsor plumbing.
Which concrete will be used to satisfy a given interface dependency, that's up to the ioc you choose to use within your factory as above. Using Windsor you may/should link interfce&concrete in the Installers.
Let me try to recap the whole process
1) Set up the routing in order to link url vs controller
2) create the container and register all components using installers
3) replace default controller factory w/ a factory working w/ your favorite ioc container(Windsor, I presume :-) )
4) Enjoy the magic.
All those steps happend in the Application_start within the Global.asax
Background : We are using MVC4 and using WIF for Claims/Authorization. We are using Moq/MvcContrib for Mockup Objects. I have looked here and created the MockIdentity and MockPrincipal Objects - do I need them?
Goal : I have a controller class that has a class level attribute that only allows users with 'Manager' claim to access the actions. I want to create mock users and test to see if anyone that doesn't have 'Manager' claim can access the actions or not.
I get the mock concept but I have only dealt with the data objects mocking and having a tough time figuring out what plugins/classes/methods/setups I need in place to do what I need to do.
Thanks in advance.
I want to create mock users and test to see if anyone that doesn't have 'Manager' claim can access the actions or not.
No, you don't. You just want to pass users to that attribute you wrote and test that sets the filterContext.Result correctly. That's it. You don't need to test that System.Web.Mvc works. Single unit under test!
Presumably your attribute is an AuthorizeAttribute, correct? So you need to test OnAuthorization(AuthorizationContext).
Disclaimer: I haven't used moq in a while, but your code would presumably look generally like this:
var user = new Mock<IPrincipal>();
user.Setup(/* whatever you need to look at */);
var authContext = new Mock<AuthorizationContext>();
authContext.Setup(ac => ac.HttpContext.User).Returns(user);
var myAttribute = new RequireManagerAttribute();
myAttribute.OnAuthorization(authContext);
authContext.VerifySet(ac => ac.Result = /* whatever you expect */);
I have came across an issue where i am unable to find a solution.I am working on a web-application and have to impliment Oauth, things are working fine for me except one issue,in my redirect back URL from Yahoo i am getting several parametersand i need to access few of them in my action class.
Now i can easily create a property in my action class with its getter and setter methods but the name of the property is
openid.response_nonce
and my Eclipse editor will not allow me to name a variable like this.Though one solution is add RequestAware interceptor in my action class and access the parameter.
my Question is can i access it without using RequestAware inteceptor?
There isn't a RequestAware interceptor... There is a Servlet-Config interceptor which will check if your action has one of the following interfaces: ServletContextAware, ServletRequestAware, ServletResponseAware, ParameterAware, RequestAware, SessionAware, ApplicationAware, PrincipalAware.
The Servlet-Config interceptor is part of the default-stack, which you are probably already using. So there is no additional cost or configuration required to use one of the aware interfaces.
That aside, if you have a parameter called "openid.response_nonce" which contains a string, you should be able to refer to it with:
//following not tested, and not checked for syntax errors
private Map openid = new HashMap();
//In Constructor{
oauth.put("response_nonce","");
}
//create BOTH a getter and setter for openid
public getOpenid(){
return openid;
}
public setOpenid(Map openid){
this.openid = openid;
}
Now struts2 should be able to figure out how to set the value... I think, sorry didn't test it. You could always create a class called Openid with a response_nonce property(along with the appropriate getters and setters for that Class)... but I think in this case it might be best to just use RequestAware if you only need that single property.
I think that you maybe looking for the Alias interceptor. http://struts.apache.org/2.0.14/docs/alias-interceptor.html
Regards