I want to override Azure AD SSO login for a MVC web application. I don't want to log other applications out in the process, but require login for security purposes. I am using OAuth2.0 with OIDC via Owin for authentication.
I am trying to use the prompt=login query parameter, which should theoretically do the trick. I found a Github reference to this being recently made available in Core but cannot trace how to do it in MVC5.2
Is it possible to do it in the Application Builder? I tried adding .WithExtraQueryParameters ("prompt=login") to the ConfidentialClientApplicationBuilder when getting the access code. No luck.
Is there another workaround if the code doesn't come out-of-the-box?
EDIT: PublicClientApplication allows .WithPrompt(Prompt.Login) while ConfidentialClientApplication does not (also does not allow AcquireTokenInteractive) This is a web app so it needs the confidential builder. Tested using the Public builder and it logs in successfully. But I get an ActiveX instantiation error 'not in a single-threaded apartment'(?) Strange, unless that is how the token is being delivered perhaps. I also tested by changing to multitenant in Azure and by toggling Public client on and off.
Any ideas?
You could use ForceLogin to add paramter to ConfidentialClientApplicationBuilder.
ForceLogin: enables the application developer to have the user prompted for credentials by the service even if this would not be needed. This can be useful if Acquiring a token fails, to let the user re-sign-in. This is done by sending prompt=login to the identity provider. Again, we've seen it used in some security focused applications where the organization governance demands that the user re-logs-in each time they access specific parts of an application.
So, use the code as below:
result = await app.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.ForceLogin)
.ExecuteAsync();
The Modifier .WithExtraQueryParameters will not help you. You need to use .WithPrompt instead.Please refer article.
Example:
await PublicClientApplication
.AcquireTokenInteractive(scopes, null)
.WithAccount(CurrentUser)
.WithPrompt(Prompt.ForceLogin) // use login for your case
.ExecuteAsync();
I eventually resolved this as follows:
Under Notifications in the ConfigureAuth(IAppBuilder app) add a reference to a new task:
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
Then add the task:
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification
<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
// Forces the user to login.
if (notification.ProtocolMessage.Prompt == null)
{
notification.ProtocolMessage.Prompt = "login";
}
return Task.FromResult(0);
}
Hope that helps the next person with this issue.
Related
I am implementing authentication for a command line client application that makes a web request to a web API. If I reason correctly, I can apply Azure Active Directory native app authentication scenario.
My concern here is that setting up Azure AD will require significant effort from the client app users on setting up AAD, plus they will have to work with an interactive dialog. This gets even worse in case no human is present, as the service to service scenario is even more complicated.
Is it possible to instead rely on the credentials of the signed-in user of the client computer? Assume Windows-based client machine that is joined to a domain, say FooDomain. The server uses an OWIN-based self-host implementation, Katana.
Related questions:
OWIN Web API Windows Service - Windows Identity Impersonation
#Konrad Jamrozik. IF you are working on .NET and want to use the logged-in user in Windows domain joined (your case), and even AAD joined, my advice would be to use MSAL.NET with the Integrated Windows Authentication (IWA) override. See https://aka.ms/msal-net-iwa. The simplified code looks like this:
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
var accounts = await app.GetAccountsAsync();
AuthenticationResult result=null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
result = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes);
}
This sample explains how to register the app and provides all the details about the code: https://github.com/azure-samples/active-directory-dotnet-iwa-v2
I am working on a webapi webservice that is proteted by Azure Active Directory. The webservice cumminucates heavily with Office 365 (SharePoint / Yammer) based on the user that is signed in.
To test the web api endpoints I am writing an Console App that let me sign in with my AAD credentials and then calls the endpoints. It works, but looking for something to replace this way of testing the web api. Would be great if it’s more repeatable and that I don’t have to fill in my credentials each time. I was looking for a unit test project but can’t get the Azure AD sign in to work.
Any tips how to make this easier?
The easiest way would be to define the test runner as an application in Azure AD and have it call the API with its own client id and secret.
To do that there are a few things you would need to do:
Add appRoles to your API in its manifest in Azure AD. These are application permissions.
Define your test runner, and have it require the necessary application permissions for your API.
In your test runner you should now be able to get an access token with the client id and secret of the test runner, no user authentication required.
Some setup is needed for app permissions on the API side as well, authorization must also look at the role claims.
You can find an example for defining app permissions and also handling them here: http://www.dushyantgill.com/blog/2014/12/10/roles-based-access-control-in-cloud-applications-using-azure-ad/.
More on defining app permissions: https://stackoverflow.com/a/27852592/1658906.
More info on the application manifest in AAD: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-application-manifest.
EDIT: If you must make calls on behalf of the user in the API, then this of course won't work.
In that case, I would suggest creating a user account with the necessary access for the purpose of running the tests. It would be best not to hard-code its credentials, but store them elsewhere.
If you don't want to "fill in my credentials each time", one workaround is using the Resource Owner Password Credentials Grant flow. This flow is flexible to gain a token easily. In the Console App, you could directly use user account and password to get the access token for your protected web API . The code below is for your reference :
static void Main(string[] args)
{
test().Wait();
}
public static async Task test()
{
using (HttpClient client = new HttpClient())
{
var tokenEndpoint = #"https://login.windows.net/a703965c-e057-4bf6-bf74-1d7d82964996/oauth2/token";
var accept = "application/json";
client.DefaultRequestHeaders.Add("Accept", accept);
string postBody = #"resource=https%3A%2F%2Fgraph.microsoft.com%2F //here could be your own web api
&client_id=<client id>
&grant_type=password
&username=nanyu#xxxxxxx.onmicrosoft.com
&password=<password>
&scope=openid";
using (var response = await client.PostAsync(tokenEndpoint, new StringContent(postBody, Encoding.UTF8, "application/x-www-form-urlencoded")))
{
if (response.IsSuccessStatusCode)
{
var jsonresult = JObject.Parse(await response.Content.ReadAsStringAsync());
var token = (string)jsonresult["access_token"];
}
}
}
}
But the problem is that flow will expose the username and password directly in the code, it brings potential attack risk as well and we will always avoid handling the user credential directly. So make sure you just use this flow for testing in a secure environment. You could refer to this article for more details.
I have secured my API app and I have successfully tested my ADB2C flow with the sample app I found here: https://github.com/Azure-Samples/active-directory-b2c-xamarin-native. Using that structure, I can trigger the sign-in process, and then access my protected API calls.
However I wanted to also use the WindowsAzure.Mobile sdk as a convenience. It is hinted at here: https://cgillum.tech/2016/08/10/app-service-auth-and-azure-ad-b2c-part-2/ that you can trigger the B2C flow from LoginAsync in that class but it does nothing when I call it in that way.
I also found https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-dotnet-how-to-use-client-library/ (scroll to "Authenticate users with the Active Directory Authentication Library") where I substituted the MSAL calls for getting the token. This triggers the sign-on flow, I get a good token and claims back, then I put it in some JSON and pass it like so:
AuthenticationResult ar = await App.PCApplication.AcquireTokenSilentAsync(App.Scopes, "", App.Authority, App.SignUpSignInpolicy, false);
JObject payload = new JObject();
payload["access_token"] = ar.AccessToken;
user = await App.MobileService.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
This call to LoginAsync throws
{Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException: You do not have permission to view this directory or page.
at Microsoft.WindowsAzure.MobileServices.MobileServiceHttpClient+<ThrowInvalidResponse>d__18.MoveNext () [0x0022f] in <filename unknown>:0
--- End of stack trace from previous location where exception was thrown ---
(snip)
Are they not designed to work together? Are those different kinds of tokens? The reason I'm using B2C is because I really don't WANT to know all that OAUTH stuff :)
In the case of B2C, you are actually getting back an ID token instead of an access token, and I believe the ar.AccessToken property would be null. This property also seems to go away in the latest versions of MSAL.
I suspect you just need to update the payload to "authenticationToken" and instead use ar.IdToken.
I am not sure if you can continue to use the "access_token" key in the payload, but it may be that you can. If not, try "authenticationToken" instead.
How can I create a login page using Xamarin.Forms?
I'm debugging on Android device and
on the page I try to query the given username and password from an MSSQL database in MSSQL
and if the login be successful, the page will navigate to a new page, else will show authentication failed.
How can I do this in Xamarin.Forms?
(Xamarin LoginFlow Example linked at bottom of answer)
You would have multiple Xamarin.Forms pages and use a NavigationPage to push or pop them from the (lifecycle) stack.
Ref: https://developer.xamarin.com/api/type/Xamarin.Forms.NavigationPage/
Think of each Page a complete self-contained mini application. So the Login page might handle the getting the UserID/Password interactively from the user, performing a authorization check via a server via a Rest api that performs your SQL query. If login is successful it pushes a new Forms-based page on the NavigationPage stack. i.e.
SplashScreen -> LoginPage -> MainPagePage
LoginFlow Xamarin example
This sample demonstrates how to manipulate the navigation stack in order to only display the main page of the application once the user has successfully logged in.
For more information about the sample see Hierarchical Navigation.
What you need is the following functionalities:
Web APIs (Web Service for Login - Hosted on internet or local network - Connected with your MSSql Server)
A Login Screen in Xamarin.Forms Application (You can design it on your own or use the above mentioned demo)
On Login Screen's 'Login' button click, you need to call the Login Web Service from Xamarin.Forms application.
This requires networking (Internet Permission on Android Project, through Manifest.xml). You can use Microsoft.Net.Http for Web Service Calls.
(Look at this example http://www.c-sharpcorner.com/UploadFile/nirmal.hota/consuming-restful-web-service-in-xamarin-forms-project-using/ )
If your server responses with JSON and you need to parse JSON then you can use Json.Net (Newtonsoft.Json).
Note: What I am trying to tell you is that you need to use Web Services that are connected to the database and hosted on a server. From Xamarin.Forms application, you have to call this web-service with appropriate parameters. This requires networking, so you can use Microsoft.Net.Http (or there are other plugins also available). To get response from the Web Service, if it is in Json then you can use Newtonsoft.Json to parse Json.
There is a full example of an app with login page: xamarin-forms-samples/LoginDemo/ provided by Xamarin.
So lets say you have a LoginController on asp.net web api (if you don't know how to create an asp.net web api project etc. this is not the place to learn it:) )
[RoutePrefix("v1/login")]
public class LoginController : ApiController
{
[Route("")]
[HttpPost] //I'm using post!!!!!! you may use get etc.
public IHttpActionResult Login(UserLoginData request)
{
var userData = CheckFromDb(request);
return Json(userData);
}
}
It checks your request from db (UserLoginData is a simple class lets say holds username and password), than if user exists you return another class (lets say it is UserData hat holds name,surname, birthday etc.). If it can not find login it may return null. It's up to you.
So it will be available on your host machine like
localhost:34252/v1/login
(34252 your port-may change for you)
So you have to call it from the device (xamarin.forms) code like this
public string Post(string url, UserLoginData userLoginData)
{
//url is login url as defined above and userLoginData holds your
//user interface (textbox) data :)
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent content;
content = new StringContent(JsonConvert.SerializeObject(userLoginData), Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(url, content).Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("call failed");
}
//this is your response as json
var responseString = response.Content.ReadAsStringAsync().Result;
//you can convert it and check to a specific class if you need
var userData = JsonConvert.DeserializeObject<UserData>(responseString);
}
}
So UserLoginData and UserData classes both must exists on both sides. You can use Newtonsoft.Json library for json parsing (jsonconvert is it's method). Hope this helps you
And android device simulator networking is a bit tricky. You may not connect to localhost directy. It may take an address like 10.0.2.* for androidemulator or 10.71.34.* for xamarin simulator. Please check your network cards for ip addresses and findthe correct one via "cmd.exe" and "ipconfig /all" command.
I'm using Windows azure access control service with custom STS. I can login to my application through ACS, but I have trouble with logout function. I've tried this code in my application.
WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule;
try
{
FormsAuthentication.SignOut();
}
finally
{
fam.SignOut(true);
}
Page.Response.Redirect("default.aspx");
But it seems that it logout the user from ACS but not from the custom STS. What should I do to logout from STS. Where could be the problem in the appliacation (RP), ACS or in STS?
I think that ACS should ask custom STS to logout the user, but it seems it doesnt do that. What I am missing?
I have created a helper method for doing FederatedSignout, with comments in the code for what I discovered along the way (hth)
public static void FederatedSignOut(string reply = null)
{
WSFederationAuthenticationModule fam = FederatedAuthentication.WSFederationAuthenticationModule;
// Native FederatedSignOut doesn't seem to have a way for finding/registering realm for singout, get it from the FAM
string wrealm = string.Format("wtrealm={0}", fam.Realm);
// Create basic url for signout (wreply is set by native FederatedSignOut)
string signOutUrl = WSFederationAuthenticationModule.GetFederationPassiveSignOutUrl(fam.Issuer, null, wrealm);
// Check where to return, if not set ACS will use Reply address configured for the RP
string wreply = !string.IsNullOrEmpty(reply) ? reply : (!string.IsNullOrEmpty(fam.Reply) ? fam.Reply : null);
WSFederationAuthenticationModule.FederatedSignOut(new Uri(signOutUrl), !string.IsNullOrEmpty(wreply) ? new Uri(wreply) : null);
// Remarks! Native FederatedSignout has an option for setting signOutUrl to null, even if the documentation tells otherwise.
// If set to null the method will search for signoutUrl in Session token, but I couldn't find any information about how to set this. Found some Sharepoint code that use this
// Michele Leroux Bustamante had a code example (from 2010) that also uses this form.
// Other examples creates the signout url manually and calls redirect.
// FAM has support for wsignoutcleanup1.0 right out of the box, there is no need for code to handle this.
// That makes it even harder to understand why there are no complete FederatedSignOut method in FAM
// When using native FederatedSignOut() no events for signout will be called, if you need this use the FAM SignOut methods instead.
}
This code is used in a standard RP library we created for Web SSO with ACS.
The December 2012 update of ACS includes support for federated single sign-out:
Using the WS-Federation protocol. Web applications that use ACS to
enable single sign-on (SSO) with identity providers using the
WS-Federation protocol can now take advantage of single sign out
capabilities. When a user signs out of a web application, ACS can
automatically sign the user out of the identity provider and out of
other relying party applications that use the same identity provider.
This feature is enable for WS-Federation identity providers, including
Active Directory Federation Services 2.0 and Windows Live ID
(Microsoft account). To enable single sign out, ACS performs the
following tasks for WS-Federation protocol endpoints:
ACS recognizes wsignoutcleanup1.0 messages from identity providers
and responds by sending wsignoutcleanup1.0 messages to relying party
applications.
ACS recognizes wsignout1.0 and wreply messages from relying party
applications and responds by sending wsignout1.0 messages to identity
providers and wsignoutcleanup1.0 messages to relying party
applications.
From the Code Sample: ASP.NET MVC 4 with Federated Sign-out, implement an Action like this to sign out from ACS:
(Note that Windows Identity Foundation is now incorporated into .NET 4.5 Framework, that's why the new namespaces below)
using System.IdentityModel.Services;
using System.IdentityModel.Services.Configuration;
public ActionResult Logout()
{
// Load Identity Configuration
FederationConfiguration config = FederatedAuthentication.FederationConfiguration;
// Get wtrealm from WsFederationConfiguation Section
string wtrealm = config.WsFederationConfiguration.Realm;
string wreply;
// Construct wreply value from wtrealm (This will be the return URL to your app)
if (wtrealm.Last().Equals('/'))
{
wreply = wtrealm + "Logout";
}
else
{
wreply = wtrealm + "/Logout";
}
// Read the ACS Ws-Federation endpoint from web.Config
// something like "https://<your-namespace>.accesscontrol.windows.net/v2/wsfederation"
string wsFederationEndpoint = ConfigurationManager.AppSettings["ida:Issuer"];
SignOutRequestMessage signoutRequestMessage = new SignOutRequestMessage(new Uri(wsFederationEndpoint));
signoutRequestMessage.Parameters.Add("wreply", wreply);
signoutRequestMessage.Parameters.Add("wtrealm", wtrealm);
FederatedAuthentication.SessionAuthenticationModule.SignOut();
string signoutUrl = signoutRequestMessage.WriteQueryString();
return this.Redirect(signoutUrl);
}