Broken AntiforgeryToken with Microsoft MSAL - asp.net-mvc

I'm working with ASP.Net MVC and I have a problem using MSAL while authenticating a User. This is because, as we use AntiforgeryToken, when the user sign in in the page of Microsoft, the token breaks and we get an error related to the token.
My question is, is there a way to keep the token even after being redirected from Microsoft login page? Or can I recreate it?
I've search on other questions and google and found nothing.
Thank you.

Yes you can save the token in your application like this:
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Acquire the access token.
string[] scopes = new string[]{"user.read"};
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
context.Token = accessToken;
}
Alternatively, you can explicitly acquire tokens by using the acquire-token methods as described in the core MSAL library. The MSAL wrapper provides the HTTP interceptor, which will automatically acquire access tokens silently and attach them to the HTTP requests to APIs.

Related

BOT framework External client authentication

I launch the client web application from the BOT framework to process the thrid party authentication. The client application process the the third party authentication using OAuth and Owin. Is there any way to send the useridentity back to the BOT framework?
and able to get the access token from the client browser. But the same api call is not working from the BOT framework or from other client. (Ex: Httpget (clientappurl/api/GetToken)
Any ideas?
//api/GetToken ---GET
public string GetToken()
{
var identity = new ClaimsIdentity (User.Identity.AuthenticateType)
identity.AddClaim ("sub", User.Identity.GetUserName()))
AuthenticationTicket ticket = new AuthenticationTicket (identity,
AuthenticationProperties());
string token = Startup. OAuthOptions.AccessTokenFormat.Protect(ticket);
return token;
}
I think what you are looking for is called the backchannel. There is an example in this link that may be all you need. There is also another example here https://github.com/Microsoft/BotFramework-WebChat/blob/master/samples/backchannel/index.html. The backchannel will allow you to communicate between the client and the bot.

GetExternalLoginInfo() returning null

I have registered my mvc app with https://apps.dev.microsoft.com/ and (when on localhost),after updating all NuGet packages managed to authenticate users (using a clientid and secret) using microsoft authentication - that works fine! lets forget the time wasted to discover that I had to use https://localhost:xxxx/signin-microsoft - thought I had to supply my callback method endpoint.
Now, I had to do the same thing however authenticating users with my app registered on Azure Active Directory in the section App Registrations.
Note: Users are not registered in azure but on a different domain however using microsoft authentication. I just changed the client id and secret to specify those generated on azure while registering the app. My Callback method is being accessed after signing in HOWEVER, the loginInfo object which I need to read the email of my user is NULL. I made sure i had the latest updates of packages, and i tried to search spending 3 days finding only applications which make use of tenants id, authority etc.
I just need to use individual accounts signing with microsoft authentication with an application registered on AAD (Azure active directory - registered apps section). I know it works because i've seen it working with php on other apps, but with microsoft code/libraries its not.
btw i tried adding scopes, calling synchronous to no good. I also tried inspecting the incoming data and its saying access denied and i'm pretty sure that client id and secret are ok. maybe the reply url is wrong? but any other callbacks i supply result in bad request and the callback method is at least being triggered. I also enabled "Sign Users In" and "Sign in and read users profile" permissions from azure as well. I'm running out of ideas. any help would be much appreciated. thanks
Code is the same code which is given to you when creating a new mvc web application using individual accounts i.e. In Startup.Auth.cs: i have this important part and other code
var myObj = new MicrosoftAccountAuthenticationOptions()
{
ClientId = "xxx",
ClientSecret = "xxx",
};
myObj.Scope.Add("openid");
myObj.Scope.Add("email");
// myObj.Scope.Add("User.Read");
app.UseMicrosoftAccountAuthentication(myObj);
In AccountController.cs i have this method which is initializing the request to microsoft
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider,
Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl
}));
}
and this (part of a) method which is handling the callback response:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await
AuthenticationManager.GetExternalLoginInfoAsync();
//...
}
ok solved. the solution i provided earlier in my question works for an application registered on https://apps.dev.microsoft.com/ . when i registered my app on AzureAD it had to be done in a different way following this method: https://learn.microsoft.com/en-us/azure/active-directory/develop/guidedsetups/active-directory-aspnetwebapp

OAuth and OWIN Authentication - Confusion

I have been asked to create a 'Authentication/Authorization' Middle man or broker as an http,MVC web application, so that this can be used to multiple applications on our organization for authentication/Authorization purposes. Means, users will signup, Login on this broker application and once confirmed Authenticated, authorized user, he will get redirected to client applications accordingly. This is the use case.
I am choosing OAuth and OWIN to develop this broker in an MVC applicaiton, which means OAuth(Authorization) will issue access token + refresh token, once user is successfully authenticated. I use normal, simple, minimal authentication logic inside the Oauth Authorization Server's Login Controller as below :
public class AccountController : Controller
{
public ActionResult Login()
{
var authentication = HttpContext.GetOwinContext().Authentication;
if (Request.HttpMethod == "POST")
{
var isPersistent = !string.IsNullOrEmpty(Request.Form.Get("isPersistent"));
if (!string.IsNullOrEmpty(Request.Form.Get("submit.Signin")))
{
var user = Constants.Users.UserCollection.Where(u => u.Email.ToLower() == Request.Form["username"].ToLower().Trim() && u.Password == Request.Form["password"].Trim());
if (user.Count() > 0)
{
authentication.SignIn(
new AuthenticationProperties { IsPersistent = isPersistent },
new ClaimsIdentity(new[]
{ new Claim(ClaimsIdentity.DefaultNameClaimType, Request.Form["username"]),
new Claim("DisplayName", user.FirstOrDefault().DisplayName) } , "Application"));
}
}
}
return View();
}
This is the MSFT sample application I am following to develop this conceptual application.
https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server.
My question here is : I read in many articles like, its not good to use Oauth authentication, else use OPENID Connect handling authentication. To be frank, I am not used with OPENID Connect and I am not sure about the necessity of creating a OPENID Provider for my organization, Since this service will be used only by customers of our organization - less than 200,000 users. We hardly need a user signup and login, this account need to be used among different web applications of our organization. Please help me here with your inputs. Thanks in advance.
I think your question is about the benefits of OpenID Connect (OIDC) over OAuth 2.0.
OIDC builds upon OAuth 2.0 so you can use all of it's features. In a practical context, the question you should ask yourself is: Do other applications (clients, APIs), which use your "broker" (authorization server/security token service/OpenID provider) need to know something about the user, who just logged in? Do they need the ID, it's roles, username etc..? If the answer is no and you just need a signed token you are probably better of with OAuth.
If you start to include user claims (=attributes) in your access token you should at least have a look at OIDC. Also note, that even if you include claims in your access token, these are meant for the resource server (=API) and are normaly inaccessable for the client (unless you extract them and expose them on the API side - this is basically what the OIDC userinfo endpoint does).

Microsoft Graph API returning 403 to any request

I'm working on an application that, in this point, will retrieve the Office Groups that the logged in user is included and perform actions based on that info.
I'm using oAuth2.0 and the v2.0 token endpoint to get access without a user, and with the code below, I can provide administrator consent to the permissions (which were applied to the application permissions on the new Application Registration Portal https://apps.dev.microsoft.com/ and appear on the Enterprise Applications section on Azure), request the token to Azure and receive it, but even with the permissions applied and that token, I get a 403 response code (Insufficient privileges) from the Graph API to any request I try to perform.
The code for those actions is the following:
// Request Admin Consent
HttpRequestMessage adminConsentRequest = new HttpRequestMessage(HttpMethod.Get, "https://login.microsoftonline.com/" + TenantId + "/adminconsent?client_id="+ClientId+"&redirect_uri=https%3A%2F%2Flocalhost%3A44369%2FHome%2F");
var adminConsentResponse = await client.SendAsync(adminConsentRequest);
// Request Token
HttpRequestMessage tokenRequest = new HttpRequestMessage(HttpMethod.Post, "https://login.microsoftonline.com/"+TenantId+"/oauth2/v2.0/token") { Content = new FormUrlEncodedContent(tokenRequestPairs) };
var tokenResponse = await client.SendAsync(tokenRequest);
string tokenResponseBody = await tokenResponse.Content.ReadAsStringAsync();
var deserializedTokenResponse = (JObject)JsonConvert.DeserializeObject(tokenResponseBody);
string accessToken = deserializedTokenResponse["access_token"].Value<string>();
// Call Microsoft Graph API
HttpRequestMessage graphRequest = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me/memberOf");
graphRequest.Headers.Add("Authorization", "Bearer "+accessToken);
var graphResponse = await client.SendAsync(graphRequest);
string graphResponseBody = await graphResponse.Content.ReadAsStringAsync();
var deserializedGraphResponse = (JObject)JsonConvert.DeserializeObject(graphResponseBody);
Enterprise Application permissions on Azure
APP Registration Portal permissions
Can someone guide to any kind of mistake I'm making?
With the authorization token and the permissions applied, I can't see why would I get an AccessDenied response.
It's been more than 48 hours since I applied the permissions, so it's not a sync problem.
Update: So thanks to #juunas I managed to reapply the permissions and the token now shows all the permissions applied on the Application Portal (User.Read.All, Directory.Read.All and Group.Read.All), but the API still returns 403 status code (Authorization_RequestDenied).
I've tried another endpoint without the /me just to make sure that is not a reference problem, but it also returns 403 status code.
One thing that is funny is that the App was registered on the new app portal as I said, and it appears on Enterprise Applications on Azure, but not on my App Registrations, so I can only alter permissions on the new App Portal. It should be like this, since I'm using a new registration portal?
After a discussion in the comments, the problem was fixed by re-consenting the permissions similarly as shown in my blog post: https://joonasw.net/view/the-grant-requires-admin-permission (though it is written for v1).
To run admin consent again, you need to add prompt=admin_consent to the authorize URL.
Okay, so a few minutes after the update on the original post, the token was accepted by the endpoints.
The only problem is that the graph API does not recognize the ID of the user logged in to use the /me endpoints, but I bypassed that using the /{group-id}/members endpoint (in my case, it's not how I wanted but solves my problem).
Thanks #juunas for the help!

How to build secured api using ServiceStack as resource server with OAuth2.0?

I have build a OAuth2.0 Authorization server using dotnetopenauth that will manage authentication, authorization, and assign accessToken to the caller. The caller will use the access token to access the api (webservices) at resource server.
If follow the sample provided by dotnetopenauth in Resource Server, api that builded using WCF can be authenticated by OAuthAuthorizationManager
If using ServiceStack to build my api in Resource Server, how to build the authentication process that verify the incoming api request based on assigned OAuth2.0 access token? The functionality should similar to OAuthAuthorizationManager in the dotnetopenid sample and not based on login session.
Just some update
I didn't use the AuthenticateAttribute or RequiredRoleAttribute from ServiceStack.ServiceInterface.
I create 2 custom RequestFilterAttribute to replace the functions provided by AuthenticateAttribute and RequiredRoleAttribute.
In each custom RequestFilterAttribute's Execute method, I'm using method in dotnetopenauth to verify the access token.
//httpReq==req from Execute(IHttpRequest req, IHttpResponse res, object requestDto)
The code for the access token verification as following, reference the relevant documentation from both servicestack and dotnetopenauth for more info. ResourceServer is class from dotnetopenauth
HttpRequestBase reqBase = new HttpRequestWrapper((System.Web.HttpRequest)httpReq.OriginalRequest);
var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(AuthorizationServerPublicKey, ResourceServerPrivateKey));
IPrincipal ip = null;
resourceServer.VerifyAccess(reqBase, out ip);
If the ip is null then not authenticated, if not null, the incoming request is valid and can use the ip to check the role e.g. ip.IsInRole(requiredRole)
I'm not sure this is the correct way to do the checking or not, but it's works for me. Any better solution are welcome.

Resources