Reputation: 674
I'm trying to create a protected controller via Azure AD application roles.
Here is an exempt from Startup.Auth, which is basically provided by Visual Studio template:
public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}
}
});
}
Tried ApiController having attributes like:
[Authorize(Roles = "Administrators")]
// GET: api/Questions
[ResponseType(typeof(Question))]
public IHttpActionResult GetQuestions()
{
....
}
and a MVC Controller:
[Authorize(Roles = "Administrators")]
public ActionResult Index()
{
....
}
In Azure Application manifest defined the following:
"appRoles": [
{
"id": "B4531A9A-0DC8-4015-8CE5-CA1DA1D73754",
"allowedMemberTypes": ["User"],
"description": "Administrators",
"displayName": "Administrators",
"value": "Administrators",
"isEnabled": true,
"origin": "Application"
}
]
Now executing GET request for /api/Questions redirects to https://login.microsoftonline.com and user authentication seems to be successful, beside there is an infinite loop of requests between localhost and microsoft online. See below:
What is it that I am doing wrong?
Using [Authorize] works just fine.
Upvotes: 2
Views: 3234
Reputation: 45
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.Url.IsLoopback)
{
return true;
}
return base.AuthorizeCore(httpContext);
}
The above code is aiming to dismiss the localhost calls. Hope it works out for you.
Upvotes: 0
Reputation: 1662
For me, the solution above did not work. Instead, I implemented a custom AuthorizeAttribute
as described in https://identityserver.github.io/Documentation/docs/overview/mvcGettingStarted.html to avoid the infinite redirect loop.
Here is my code for the custom AuthorizeAttribute
implementation (again, largely from the resource above):
public class RoleAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// If the user is authenticated, but we ended up here, it means that
// the user is not in a role that's allowed to use the controller being called
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
else
filterContext.Result = new HttpUnauthorizedResult();
}
}
PS: I am not including the TokenValidationParameters
code and I can use both the AuthorizeAttribute
with and without Roles
specified. However, when I specify Authorize(Roles = "SomeRole")
and the already authenticated and authorized user is not in that role, I am faced with the infinite redirect loop.
Therefore, for those controller methods where I have to use roles-based authorization, I use RoleAuthorize(Roles = "SomeRole")
instead. I also have a custom 403 error page that is styled like the application and provides an appropriate amount of detail and sign out link so the user can try to authenticate as a different user.
I am not fully satisfied with that solution as I feel that the built-in AuthorizeAttribute
should be able to handle this scenario. I am just not able to get it to work.
Upvotes: 4
Reputation: 674
It turns out the following should be added to Startup.Auth:
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
// map the claimsPrincipal's roles to the roles claim
RoleClaimType = "roles",
}
It is actually configuring 'roles' claim type in order to map it with the default behavior. Excellent explanation is available at: https://samlman.wordpress.com/2015/03/09/using-roles-in-azure-applications/
Upvotes: 8