Save object in authenticationcontext asp.net core - asp.net-core

I'm converting my asp.net framework to asp.net core.
One thing I'm facing with is saving query data in Authentication context in authorizationhandler.
In my asp.net framework, I've done with my AuthorizeAttribute in ASP.Net Framework:
public override void OnAuthorization(HttpActionContext actionContext)
{
// Retrieve email and password.
var accountEmail =
actionContext.Request.Headers.Where(
x =>
!string.IsNullOrEmpty(x.Key) &&
x.Key.Equals(HeaderFields.RequestAccountEmail))
.Select(x => x.Value.FirstOrDefault())
.FirstOrDefault();
// Retrieve account password.
var accountPassword =
actionContext.Request.Headers.Where(
x =>
!string.IsNullOrEmpty(x.Key) &&
x.Key.Equals(HeaderFields.RequestAccountPassword))
.Select(x => x.Value.FirstOrDefault()).FirstOrDefault();
// Invalid account name or password.
if (string.IsNullOrEmpty(accountEmail) || string.IsNullOrEmpty(accountPassword))
{
// Treat this request is unauthorized.
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
{
Error = $"{Language.WarnAccountNotLogin}"
});
return;
}
// Find the hashed password from the original one.
var accountHashedPassword = RepositoryAccountExtended.FindMd5Password(accountPassword);
// Retrieve person whose properties match conditions.
var person = RepositoryAccountExtended.FindPerson(null, accountEmail, accountHashedPassword, null, null);
// No person has been found.
if (person == null)
{
// Treat this request is unauthorized.
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
{
Error = $"{Language.WarnAccountNotLogin}"
});
return;
}
// Account has been disabled.
if ((StatusAccount) person.Status == StatusAccount.Inactive)
{
// Treat the login isn't successful because of disabled account.
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
{
Error = $"{Language.WarnDisabledAccount}"
});
return;
}
// Account is still pending.
if ((StatusAccount) person.Status == StatusAccount.Pending)
{
// Treat the login isn't successful because of pending account.
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, new
{
Error = $"{Language.WarnPendingAccount}"
});
return;
}
// Account role isn't enough to access the function.
if (!Roles.Any(x => x == person.Role))
{
// Role isn't valid. Tell the client the access is forbidden.
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden, new
{
Error = $"{Language.WarnForbiddenAccessMethod}"
});
}
// Store the requester information in action argument.
actionContext.ActionArguments[HeaderFields.Account] = person;
}
As you see, I stored my query data (Account - in this situation) in the actionContext, and I can access to it later in Controllers.
My question is: How can I achieve the same thing in ASP.NET Core, because I don't want to query my database in my every AuthorizationHandler.
Thank you,

How can I achieve the same thing in ASP.NET Core
First you need an authentication middleware, for your case it may be basic authentication. For Aspnet Core there is no built-in basic authentication middleware. A soluton is here or you can implement own authentication middleware like this.
I stored my query data (Account - in this situation) in the
actionContext, and I can access to it later in Controllers.
Two possible ways are coming to my mind:
Adding parameter into HttpContext.Items
Adding claim to current User.Identity
To implement this you can use ClaimsTransformation or custom middleware after authentication middleware. If you go with your own implementation you can also use HandleAuthenticateAsync method.
Update
It seems right place to save query data is HandleAuthenticateAsync. If you use #blowdart's basic authentication solution, your code might be something like below:
.....
await Options.Events.ValidateCredentials(validateCredentialsContext);
if (validateCredentialsContext.Ticket != null)
{
HttpContext.Items[HeaderFields.Account] = person; // assuming you retrive person before this
Logger.LogInformation($"Credentials validated for {username}");
return AuthenticateResult.Success(validateCredentialsContext.Ticket);
}

Related

Asp.Net Core Mvc6 Claims Authorization is not persistent

I am doing an Asp.Net Core Mvc 6 App where I dynamically assign Claims to the user.
I have in the database all Roles that a user must have to access specific fields of the page.
I also have the role of the user.
I am creating Claims where the user's role matches the field page User role. And then I create a ClaimsPrincipal with those list of Roles.
This are the data in the database
This is how I create the permission for the user..
private string[] TransformRowsIntoPermissions(List<RolesAccessModel>? rows, string userRole)
{
List<string> permissionList = new();
if(rows!=null)
{
foreach (RolesAccessModel row in rows)
{
if (row.Roles!=string.Empty && row.Roles != null && !row.Roles.Contains(userRole))
continue;
// if we get here we have a match
if (!permissionList.Contains(row.EventName))
permissionList.Add(row.EventName);
}
}
return permissionList.ToArray();
}
When I have the Permission List, I add it as Claims
private Claim[] CreateClaimsFromArray(string[] permissions)
{
List<Claim> claims = new();
foreach (var permission in permissions)
claims.Add(new Claim(permission, "-"));
return claims.ToArray();
}
And in the main function I save it as ClaimPrincipal
private async void CreateClaimsByUserRole(string role)
{
ClaimsIdentity claimsIdentity =await _iUIConfig.CreateClaimsByUserRole(role);
var userPrincipal = new ClaimsPrincipal(new[] { claimsIdentity });
_ = HttpContext.SignInAsync(userPrincipal);
}
I do not know if line _ = HttpContext.SignInAsync(userPrincipal); is necessary.
I checked userPrincipal and it has all the data.
What I need to do is to ask in the View for that claims in order to show or to not Show the fields.
But when I ask if it is null...
#{
var claimsIdentity = User.Identity as System.Security.Claims.ClaimsIdentity;
if (claimsIdentity != null)
{
var c = claimsIdentity.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
if (c != null)
{
<p>
#c.Type.ToString();
#c.Value.ToString();
</p>
}
else
{
<p>
null
</p>
}
}
}
It is null
If I ask for a specific value
#{
if(System.Security.Claims.ClaimsPrincipal.Current.Claims.ToList().FirstOrDefault(c => c.Type == "DocumentId" && c.Value == "-") != null)
{
.....
}
}
Got an error System.NullReferenceException: 'Object reference not set to an instance of an object.'
Even If a call a Controller/Method and ask for
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
Is null..
I have to do it persistent for all the App.
I am not using Authentication because it is not implemented yet.. I am using SSO from another system. For that reason, for my understanding Cookie are used for Authentication.
In program.cs I only have
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminAccess", policy => policy.RequireRole("Admin"));
options.AddPolicy("ManagerAccess", policy =>
policy.RequireAssertion(context =>
context.User.IsInRole("Admin")
|| context.User.IsInRole("Manager")));
options.AddPolicy("UserAccess", policy =>
policy.RequireAssertion(context =>
context.User.IsInRole("Admin")
|| context.User.IsInRole("Manager")
|| context.User.IsInRole("User")));
});
and
app.UseAuthorization();
What am I missing to do it persistent?
Thanks
ClaimsPrincipal.Current was used in asp.net,however,in Asp.net core it is no longer set
To retrieve the current user in an ASP.NET Core MVC app,you could try with ControllerBase.User orHttpContext.User
You could check this document for more details
SignInAsync creates an encrypted cookie and adds it to the current response.You could get the claims after authenticating successfully in the following request untill the cookie expires(not the current request)
Notice HttpContext.User could be setted directly
And in View you could get the claims in # section as below:
#{
var claims = this.ViewContext.HttpContext.User.Claims.ToList();
}

ASP.NET Identity with Sustainsys Saml2 - How to persist ExternalLoginInfo Claims?

I have an ASP.NET Core app, targeting netcoreapp3.1, set up with ASP.NET Identity and the Sustainsys.Saml2.AspNetCore2 package.
IDP-initiated SAML authentication is working fine, but I can't retrieve custom attributes/claims from the signed-in user after the authentication redirect.
In my ExternalLogin.cshtml.cs class, the custom claims are present on the ExternalLoginInfo.Principal (as var info in the code below), but they are not retrievable from Context.User.Claims after the redirect.
That is, _logger.LogInformation($"PatientId: {info.Principal.FindFirst("PatientId")}"); prints the value passed in the custom PatientId SAML attribute, but #Context.User.FindFirst("PatientId"); is null after the redirect.
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
var info = await _CustomSignInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _CustomSignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation($"{info.Principal.Identity.Name} logged in with {info.LoginProvider} provider.");
_logger.LogInformation($"PatientId: {info.Principal.FindFirst("PatientId")}");
return LocalRedirect(returnUrl);
}
...
}
My conclusion from this answer is that these claims should still be available. Do I need to somehow pass the ExternalLoginInfo.Principal (rather just the LoginProvider and ProviderKey) to the ExternalLoginSignInAsync method?
I will say that I only want to persist the claims for the length of the session, not add them as AspNetUserClaims the database. They will be different on each login.
I figured out something that works. My question at the end there, "Do I need to somehow pass the ExternalLoginInfo.Principal (rather just the LoginProvider and ProviderKey) to the ExternalLoginSignInAsync method?" was on the right track.
As you can see above, the ExternalLoginSignInAsync method is in the CustomSignInManager class (it's a custom implementation for reasons not relevant to this question, but that method was unchanged from the default ASP.NET Identity SignInManager).
Tracing the request path to the "bottom," ExternalLoginSignInAsync calls SignInOrTwoFactorAsync, which calls SignInAsync. In SignInAsync, a new ClaimsIdentity is created and returned. So I simply had to pass them from the ExternalLoginInfo.Principal instance through all of those methods such that I could set them on the new principal.
Potentially helpful minutiae: I chose to create a CustomClaimSet class to store the custom attributes, and then I had to update all the method signatures to accept a CustomClaimSet as an argument.

Identity Server 4 User Impersonation

I am struggling to implement a Impersonation feature into the Identity Server 4 Service. I understand that there's a lot of people who are against implementing it the way I want to but I really need the full redirect back to the SSO server in order to generate a new list of claims. The user that is being impersonated will have a completely different set of Rules associated with them as claims, so it must come from the IdSrvr.
I have read through https://github.com/IdentityServer/IdentityServer4/issues/853, and also IdentityServer4 - How to Implement Impersonation
Here's what I've attempted so far, We did this perfectly inside of Identity Server 3 with ACR values and the Pre-Auth even on the UserService.
This controller method I am calling from one of the Clients of my identity server:
public IActionResult Impersonate(string userIdToImpersonate, string redirectUri)
{
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties(){RedirectUri = redirectUri, Items = { {"acr_values", $"userIdToImpersonate:{userIdToImpersonate}"}}});
}
Here is my OnRedirectToProvider:
OnRedirectToIdentityProvider = context =>
{
if (context.Properties.Items.ContainsKey("acr_values"))
{
context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"].ToString();
}
return Task.CompletedTask;
}
This is where i start to get lost, at the moment, I've inherited from the AuthorizeInteractionResponseGenerator class and implemented my own with the override to the ProcessLoginAsync (this is the only thing i could find that was close to the pre-auth event previously)
protected override async Task<InteractionResponse> ProcessLoginAsync(ValidatedAuthorizeRequest request)
{
if (!request.IsOpenIdRequest) return await base.ProcessLoginAsync(request);
var items = request.GetAcrValues();
if (items.Any(i => i.Contains("userIdToImpersonate")) && request.Subject.IsAuthenticated())
{
//handle impersonation
var userIdToImpersonate = items.FirstOrDefault(m => m.Contains("userIdToImpersonate")).Split(':').LastOrDefault();
request.Subject = await _signInManager.ImpersonateAsync(userIdToImpersonate);
//var userToImpersonate = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);
//if (userToImpersonate == null) return await base.ProcessLoginAsync(request);
//var userBeingImpersonated = await _signInManager.UserManager.FindByIdAsync(userIdToImpersonate);
//var currentUserIdentity = await _signInManager.CreateUserPrincipalAsync(userBeingImpersonated);
//var currentClaims = currentUserIdentity.Claims.ToList();
//currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, request.Subject.IsBeingImpersonated() ? request.Subject.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : _signInManager.UserManager.GetUserId(request.Subject)));
//request.Subject = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));
//return await base.ProcessLoginAsync(request);
return new InteractionResponse();
}
else
{
return await base.ProcessLoginAsync(request);
}
}
As you can see, i've tried a couple different things here, When not using OIDC as a authentication scheme, and my IdServer/Site is the same site, I had a function that impersonation worked with. Which is where _signInManager.ImpersonateAsync(...) is. Here is that Implementation:
public async Task<ClaimsPrincipal> ImpersonateAsync(string userIdToImpersonate)
{
var userBeingImpersonated = await UserManager.FindByIdAsync(userIdToImpersonate);
if (userBeingImpersonated == null) return null;
var currentUserIdentity = await CreateUserPrincipalAsync(userBeingImpersonated);
var currentClaims = currentUserIdentity.Claims.ToList();
currentClaims.Add(new Claim(IdentityServiceClaimTypes.ImpersonatedById, Context.User.IsBeingImpersonated() ? Context.User.GetClaimValue(IdentityServiceClaimTypes.ImpersonatedById) : UserManager.GetUserId(Context.User)));
//sign out current user
await SignOutAsync();
//sign in new one
var newIdentity = new ClaimsPrincipal(new ClaimsIdentity(currentClaims));
await Context.SignInAsync(IdentityConstants.ApplicationScheme, newIdentity);
return Context.User;
}
In an effort to simply 'replace' who was signing in, or at least who the identity server was thinking was signing in, i just replaced Request.Subject with the Impersonation Result. This doesn't actually change anything that I can find, at least not on my client app. If i use the redirect URI of 'https://localhost:44322/signin-oidc' (localhost because i'm running the sites locally), I get a "Correlation failed at signin-oidc redirect" message. If anyone has implemented something like this or done anything similar I would greatly appreciate the help getting this complete.
Suggestions welcome for completely different implementations, this was just my best stab at what worked flawlessly with idsrvr3.

Extended user login

based on the ASP.NET Core Visual Studio emplate with individual user accounts and adding IdentityServer4 to the project as described in IdentityServer4: Using ASP.NET Core Identity, the relevant part for the login in the account-controller looks something like this:
ApplicationUser user = null;
// login with username and password
if (!string.IsNullOrEmpty(model.Username) && !string.IsNullOrEmpty(model.Password))
{
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true);
if (result.Succeeded)
{
user = await _userManager.FindByNameAsync(model.Username);
}
}
if (user != null)
{
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
// redirect
}
With this login, identityserver4 correctly issues the authentication scheme "Cookies" and creates the required tokens (access_token, id_token or whatever is configured).
Now to the problem I am having: I want to extend the username/password login with an additional option, namely an access key login. This means, that the user provides only a key he owns (similar to a password).
Please put aside all security and use-case concerns you might now have.
My code looks something like this:
// login with access key
if (!string.IsNullOrEmpty(model.AccessKey))
{
var result = await _signInManager.AccessKeySignInAsync(model.AccessKey);
if (result.SignInResult.Succeeded)
{
user = result.User;
}
}
if (user != null)
{
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
// redirect
}
Simply put, I have extended the SignInManager and the UserManager with the functionality i needed. The login "logic" works correctly (the UserLoginSuccessEvent is fired properly with the expected user). However, IdentityServer4 does not seem to notice any of this. The log pretty much stays empty (except the mentioned event). My question now is: How do I inform IdentityServer4 that this was a successful user login and the tokens can be issued?
In case you need the implementation of the SignInManager.AccessKeySignInAsync method:
public async Task<(SignInResult SignInResult, ApplicationUser User)> AccessKeySignInAsync(string accessKey)
{
if (accessKey == null)
{
throw new ArgumentNullException(nameof(accessKey));
}
var user = await _userManager.FindByAccessKeyAsync(accessKey);
return (user != null ? SignInResult.Success : SignInResult.Failed, user);
}

Using Roles with Forms Authentication

I'm using forms authentication in my MVC application. This is working fine. But not I want to adjust authorization to only allow people in certain roles. The logins correspond to users in active directory and the roles correspond to the groups the users are in.
For authentication, I simply call FormsAuthentication.SetAuthCookie(username, true) after verifying the login.
For authorizing, I first applied the attribute to the controllers I want to secure
[Authorize(Roles = "AllowedUsers")]
public class MyController
...
Next, I'm handling the OnAuthenticate event in global.asax.
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
// Create WindowsPrincipal from username. This adds active directory
// group memberships as roles to the user.
args.User = new WindowsPrincipal(new WindowsIdentity(ticket.Name));
FormsAuthentication.SetAuthCookie(ticket.Name, true);
}
catch (Exception e)
{
// Decrypt method failed.
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " + "supported for this application.");
}
}
With this when someone accesses the website they get the login screen. From there they can actually log in. However, somehow it doesn't save the auth cookie and they get a login screen after the next link they click. I tried adding a call to SetAuthCookie() in OnAuthenticate() but they made no difference.
Before I added this event handler to handle authorization, authentication worked fine. So somewhere in the framework User is being set. I'm wondering if this the correct approach and I'm just missing something or if I need a different approach.
What do I need to do to get this to work?
Thanks,
Scott
It seems like my initial approach won't work. I was trying to get ASP.NET to automatically load user roles from their AD account. No comment was given on whether this was possible. However, the research I've done indicates I'll have to write code to load AD group memberships into user roles.
The solution to creating the user principal that ASP.NET MVC uses appears to be to create it in FormsAuthentication_OnAuthenticate() and assign it to Context.User. It appears if I don't set Context.User ASP.NET MVC creates a user principal based off the auth ticket after FormsAuthentication_OnAuthenticate() returns. Additionally, ASP.NET MVC appears to do nothing with Context.User if I set it in FormsAuthentication_OnAuthenticate().
The following is what I ended up doing.
This is the code that handles authentication
public ActionResult LogOn(FormCollection collection, string returnUrl)
{
// Code that authenticates user against active directory
if (authenticated)
{
var authTicket = new FormsAuthenticationTicket(username, true, 20);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
authCookie.Expires = DateTime.Now.AddMinutes(30);
Response.Cookies.Add(authCookie);
if (Url.IsLocalUrl(returnUrl)
&& returnUrl.Length > 1
&& returnUrl.StartsWith("/", StringComparison.OrdinalIgnoreCase)
&& !returnUrl.StartsWith("//", StringComparison.OrdinalIgnoreCase)
&& !returnUrl.StartsWith("/\\", StringComparison.OrdinalIgnoreCase))
{
return Redirect(returnUrl);
}
else
{
return Redirect("~/");
}
}
return View();
}
I initially tried just calling FormsAuthentication.SetAuthCookie(username, true) instead of manually creating, encrypting, and adding it to the Response cookie collections. That worked in the development environment. However, it didn't after I published to the website.
This is the log off code
public ActionResult LogOff()
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
authCookie.Expires = DateTime.Today.AddDays(-1);
}
Response.Cookies.Add(authCookie);
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
FormsAuthentication.SignOut() doesn't seem to do anything after I switched to manually creating, encrypting, and adding the auth ticket to the response cookie collection in the logon code. So I had to manually expire the cookie.
This is the code I have for FormsAuthentication_OnAuthenticate()
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs args)
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || string.IsNullOrWhiteSpace(authCookie.Value))
return;
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
UserData userData = null;
if (Application["UserData_" + authTicket.Name] == null)
{
userData = new UserData(authTicket.Name);
Application["UserData_" + authTicket.Name] = userData;
}
else
{
userData = (UserData)Application["UserData_" + authTicket.Name];
}
Context.User = new GenericPrincipal(new GenericIdentity(authTicket.Name), userData.Roles);
}
UserData is a class I created to handle caching user roles. This was needed because of the time it takes for active directory to return the group memberships the user belongs to. For completeness, the following is the code I have for UserData.
public class UserData
{
private int _TimeoutInMinutes;
private string[] _Roles = null;
public string UserName { get; private set; }
public DateTime Expires { get; private set; }
public bool Expired { get { return Expires < DateTime.Now; } }
public string[] Roles
{
get
{
if (Expired || _Roles == null)
{
_Roles = GetADContainingGroups(UserName).ToArray();
Expires = DateTime.Now.AddMinutes(_TimeoutInMinutes);
}
return _Roles;
}
}
public UserData(string userName, int timeoutInMinutes = 20)
{
UserName = userName;
_TimeoutInMinutes = timeoutInMinutes;
}
}
Roles can also be stored in a cookie and you have at least two options:
a role provider cookie (another cookie that supports the forms cookie), set with cacheRolesInCookie="true" on a role provider config in web.config. Roles are read the first time authorization module asks for roles and the cookie is issued then
a custom role provider that stores roles in the userdata section of the forms cookie, roles have to be added to the user data section of the forms cookie manually
The Authorization module asks the current principal for user roles, which, if role provider is enabled, either scans the role cookie (the first option) or fires the custom role provider methods.
Yet another, recommended approach is to switch to the Session Authentication Module (SAM) that can replace forms authentication. There are important pros, including the fact that SAM recreates ClaimsPrincipal out of the cookie and roles are just Role claims:
// create cookie
SessionAuthenticationModule sam =
(SessionAuthenticationModule)
this.Context.ApplicationInstance.Modules["SessionAuthenticationModule"];
ClaimsPrincipal principal =
new ClaimsPrincipal( new GenericPrincipal( new GenericIdentity( "username" ), null ) );
// create any userdata you want. by creating custom types of claims you can have
// an arbitrary number of your own types of custom data
principal.Identities[0].Claims.Add( new Claim( ClaimTypes.Role, "role1" ) );
principal.Identities[0].Claims.Add( new Claim( ClaimTypes.Role, "role2" ) );
var token =
sam.CreateSessionSecurityToken(
principal, null, DateTime.Now, DateTime.Now.AddMinutes( 20 ), false );
sam.WriteSessionTokenToCookie( token );
From now on, the identity is stored in a cookie and managed automatically and, yes, the Authorization attribute on your controllers works as expected.
Read more on replacing forms module with SAM on my blog:
http://www.wiktorzychla.com/2012/09/forms-authentication-revisited.html