How to control the login flow in ADAL AuthenticationContext? - azure

Using the ADAL library for getting a token for WAAD i would like to know how I can get more control over the login flow.
var ac = new AuthenticationContext("https://login.windows.net/" + ActiveDirectoryTenantId);
AuthenticationInfo = ac.AcquireToken(
resource: "https://management.core.windows.net/",
clientId: "1950a258-227b-4e31-a9cf-717495945fc2",
redirectUri: new Uri("urn:ietf:wg:oauth:2.0:oob"));
The user is prompted to login. For me it's via Live Id, for my customer's computer it's via an organizational account, and there is no way to switch between them. It seems to be controlled by how/what current sessions the computer might have running already logged into azure.
Can I do anything in the AcquireToken call to control this?
It would be best if I could trigger the normal flow when people log into Azure where they get to select if its a live id or a organizational login.
I have tried this:
ac.AcquireToken("https://management.core.windows.net/",
"1950a258-227b-4e31-a9cf-717495945fc2",
new Uri("urn:ietf:wg:oauth:2.0:oob"), PromptBehavior.Always,"wtrealm=urn:federation:MicrosoftOnline");
with no luck.

I found some magic tricks that seems to give some more control.
// ID for site to pass to enable EBD (email-based differentiation)
// This gets passed in the call to get the azure branding on the
// login window. Also adding popup flag to handle overly large login windows.
internal const string EnableEbdMagicCookie = "site_id=501358&display=popup";
private void ClearCookies()
{
NativeMethods.InternetSetOption(IntPtr.Zero, NativeMethods.INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);
}
private static class NativeMethods
{
internal const int INTERNET_OPTION_END_BROWSER_SESSION = 42;
[DllImport("wininet.dll", SetLastError = true)]
internal static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer,
int lpdwBufferLength);
}

Related

How to authenticate our .NET console application against SharePoint online if we have `DisableCustomAppAuthentication` set to true

We have the following:-
SharePoint online tenant recently created
Windows server 2019
.NET console application which have some code that integrates with SharePoint online
The .NET console application runs on schedule basis using windows task scheduler.
now previously on old tenants i authenticate my code using this method by passing the ClientID and Client Secret:-
static void Main(string[] args)
{
string siteUrl = "https://***.sharepoint.com/sites/CustomerServiceKB/";
string clientId = "******";
string clientSecret = "*****";
using (ClientContext context = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(siteUrl, clientId, clientSecret))
{
but on our newly created tenant we can not authenticate our code using the above method, because we have the DisableCustomAppAuthentication set to true.. now we do not want to modify this property.
So our question is; if we have the DisableCustomAppAuthentication set to true (ans we do not want to set it to false), then how we can authenticate our console application? which is hosted inside our windows server and which runs on schedule basis using tasks scheduler ?
So the DisableCustomAppAuthentication property was brought in (and was set to true by default) to support the deprecation of the Azure Access Control Service (ACS). The modern way to authenticate custom apps in Sharepoint tenants is to register them in the Azure AD.
Before moving on, consider the pros and cons of switching to the new authentication scheme. Mainly, the ACS enables users to granularly control site-level permissions for the application authentication, but Azure AD app registration makes the set of running applications transparent to the administrators. If you want to stay with ACS, just set the DisableCustomAppAuthentication to false.
Now, if you decided to move on with registering the application in the Azure ID, here are the steps to follow:
Log in to the Azure portal and navigate to the Azure Active Directory.
Register the application in the Azure AD portal. Here's a guide on how to do it. Obtain the application (client) ID on the application overview page.
Set all the necessary permissions, confirm them as a global administrator (or ask the administrator for confirmation).
Configure the authentication. Choose the authentication option: whether you want the application to authenticate itself in the Microsoft IAM via a cryptographic certificate or a client secret.
Obtain your Azure Active Directory tenant ID (not to be confused with the Sharepoint Online tenant ID). Here's how to do in in the Azure AD; there's also a hacky way.
Now use the client ID (from step 2), the authentication option (step 4), and the AAD tenant ID (step 5) to authenticate and run your application:
using Microsoft.Identity.Client;
[..]
private static async Task<string> GetToken()
{
string applicationId = "client-id";
string tenantId = "aad-tenant-id";
bool isUsingClientSecret = <true or false>;
IConfidentialClientApplication app;
if (isUsingClientSecret)
{
string secret = "secret";
app = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithClientSecret(secret)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.Build();
}
else
{
string certificateLocation = "certificate-file";
X509Certificate2 certificate = ReadCertificate(certificateLocation);
app = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithCertificate(certificate)
.WithAuthority($"https://login.microsoftonline.com/{tenantId}")
.Build();
}
var scopes = new[] { "https://***.sharepoint.com/.default" };
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
return authenticationResult.AccessToken;
}
static async Task MainAsync(string[] args)
{
string site = "https://***.sharepoint.com/sites/CustomerServiceKB";
string token = await GetToken();
using (ClientContext context = new ClientContext(site))
{
context.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + token;
};
Web web = context.Web;
context.Load(web);
context.ExecuteQuery();
}
}
static void Main(string[] args)
{
try
{
AsyncContext.Run(() => MainAsync(args));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw;
}
}

Preventing ClaimsTransformation from running on every HTTP request

I have a web application targeting .NET 5.0 and I am trying to implement Windows Authentication along with some policies for authorization. We have a database table from another application that holds info on user roles, so I am using that to set permissions for my users.
I created a ClaimsTransformer class:
ClaimsTransformer.cs
public class ClaimsTransformer : IClaimsTransformation
{
// snip constructor which pulls in my DbContext from DI
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var id = ((ClaimsIdentity) principal.Identity);
var ci = new ClaimsIdentity(id.Claims, id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
// snip call to DbContext to get user's role from database
if (roleId == 1 || roleId == 7)
{
ci.AddClaim(new Claim("user-role", "admin"));
}
return new ClaimsPrincipal(ci);
}
}
I have my authentication/authorization setup like this:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
services.AddAuthorization(options =>
{
options.AddPolicy("Admin", policy =>
policy.RequireClaim("user-role", "admin"));
});
// snip rest of method
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// snip unrelated code
app.UseAuthentication();
app.UseAuthorization();
}
My first use of this is to hide a menu in my _Layout.cshtml:
#if ((await AuthorizationService.AuthorizeAsync(User, "admin").Succeeded)
{
// Admin nav link
}
The issue is, since AuthorizeAsync is running on every HTTP request, my ClaimsTransformer also runs each time, hitting the database to check the user's roles on every request. I'd like to avoid this, but I'm not sure of the best way to do so.
Basically, I'd like the system to check the roles only once, when the user is first authenticated. I read that this is what is supposed to happen when using Windows Authentication with IIS, but I am seeing the roles query running on every request when deployed to my IIS server also.
I could easily add a check in my ClaimsTransformer to see if the "user-role" claim exists, and only hit the DB if it is not present, but is there a better way? Should I be overriding something like UserClaimsPrincipalFactory instead of using a ClaimsTransformer?

Google SignIn returns Current user as null

I am using google login for my Xamarin iOS app, however after successfully entering the username and password in the safari browser prompt that appears, It returns properties CurrentUser as null.
My Code:
Google.SignIn.SignIn.SharedInstance.Delegate = this;
Google.SignIn.SignIn.SharedInstance.UIDelegate = this;
Google.SignIn.SignIn.SharedInstance.SignInUser();
Google.SignIn.SignIn.SharedInstance.SignedIn += SharedInstance_SignedIn;
private void SharedInstance_SignedIn(object sender, SignInDelegateEventArgs e)
{
var signin = (Google.SignIn.SignIn)sender;
//Here if i debug signin variable, its property CurrentUser comes as null
}
I am using
Xamarin.IOS.Google.Signin: v4.0.1.1
Am I missing some setting that needs to be enabled in the Google api console?
Sender indicate the class that raised the event.
To get User ,you can use e.User Or Google.SignIn.SignIn.SharedInstance.CurrentUser
here explatins the function of method SignIn
The Sign-In SDK automatically acquires access tokens, but the access tokens will be refreshed only when you call SignIn or SignInSilently methods.

Azure MSAL JS: How to edit profile?

I've successfully implemented MSAL JS for Azure AD B2C.
The next step is to let the user edit their profile. I've created a new policy for Edit Profile.
But how to redirect the user there? There are only login methods / acquire token methods.
I've tried to set the authority to a different policy. It then does redirect to the right page, but then it starts complaining about errors in scopes, and it messes up the token locally.
editProfile() {
this.userAgentApp.authority = this.policyEditProfile;
this.userAgentApp.loginRedirect();
}
The ASP.NET code examples explicitly have an option to set the editProfile Policy ID: https://learn.microsoft.com/en-gb/azure/active-directory-b2c/active-directory-b2c-devquickstarts-web-dotnet-susi#update-code-to-use-your-tenant-and-policies
Feels like this is missing from MSAL.JS and I have to manually craft the URL, is that correct?
Yes, this is correct. You will need to use a different authority which URL is composed of the tenant and the policy name, as shown here:
private static string Tenant = "yourTenant.onmicrosoft.com";
public static string PolicySignUpSignIn = "b2c_1_susi";
public static string PolicyEditProfile = "b2c_1_edit_profile";
private static string BaseAuthority = "https://login.microsoftonline.com/tfp/{tenant}/{policy}/oauth2/v2.0/authorize";
public static string Authority = BaseAuthority.Replace("{tenant}", Tenant).Replace("{policy}", PolicySignUpSignIn);
public static string AuthorityEditProfile = BaseAuthority.Replace("{tenant}", Tenant).Replace("{policy}", PolicyEditProfile);
BTW, that sample, although for .NET Desktop shows how to use the edit profile and password reset policies: active-directory-b2c-dotnet-desktop , see in particular the EditProfileButton_Click method, the factor of acquiring the token (interactively) will trigger the dialog to edit the profile:
AuthenticationResult authResult = await App.PublicClientApp.AcquireTokenAsync(App.ApiScopes, GetUserByPolicy(App.PublicClientApp.Users, App.PolicyEditProfile), UIBehavior.SelectAccount, string.Empty, null, App.AuthorityEditProfile);

How to Deactivate a LDAP User?

I am using a library to authenticate LDAP Users, whose code is as follows:
public void authUser(String username, String pwd)
throws Exception
{
try
{
Properties env = getEnvironmentForContext();
env.put("java.naming.security.principal", "uid=" +
username + ",ou=users, dc=company"));
env.put("java.naming.security.credentials", pwd);
context = getContext(env);
System.out.println("Authentication Succeeded");
}
catch (Exception e)
{
System.out.println("Authentication Failed");
throw e;
}
}
Please note, i cannot modify the above Authentication Code. It comes from a external Library.
But, i want to deactivate some users (not delete them), so that Authentication Fails.
I am using LDAP (not Active Directory). Do not know what LDAP Software it is though, i can connect to it using 'LDAP Browser Client'.
The users exist under: dc=company, ou=users, uid=username
What attribute can i add/change on LDAP 'user' to de-activate a user.
Could i move the user to a different group like: dc=company, ou=deactivatedusers, uid=username? But this is not the preferred option, plus am not sure best way to do that.
EDIT: The LDAP being used is: Netscape/Sun/iPlanet
To answer your question per the Oracle iPlanet (Sun) documentation :
Setting the attribute nsAccountLock to true will disable a users account, and prevent them from binding to the directory.
However, in terms of the code you already have, I just don't see any way of accomplishing this... Is there something preventing you from writing your own implementation for iPlanet using the System.DirectoryServices.Protocols namespace in .Net?
Here is how I bind and authorize users against an iPlanet server :
//Build servername from variables
var BuildServerName = new StringBuilder();
BuildServerName.Append(ServerName);
BuildServerName.Append(":" + Convert.ToString(Port));
var ldapConnection = new LdapConnection(BuildServerName.ToString());
//Authenticate the Admin username and password, making sure it's a valid login
try
{
//Pass in the network (administrative) creds, and the domain.
var networkCredential = new NetworkCredential(Username, Password, config.LdapAuth.LdapDomain);
ldapConnection.SessionOptions.SecureSocketLayer = true;
ldapConnection.SessionOptions.VerifyServerCertificate += delegate { return true; };
ldapConnection.AuthType = AuthType.Anonymous;;
ldapConnection.Bind(networkCredential);
//Lets find this person so we can use the correct DN syntax when we authorize them.
SearchRequest FindThem = new SearchRequest();
FindThem.Filter = config.LdapAuth.LdapFilter.Replace("{{Patron}}", Patron);
FindThem.DistinguishedName = config.LdapAuth.LdapDomain;
FindThem.Scope = System.DirectoryServices.Protocols.SearchScope.Subtree;
//We'll execute a search using the bound user
SearchResponse searchresults = (SearchResponse) ldapConnection.SendRequest(FindThem);
//Should only get on result back, if not throw an error
if(searchresults.Entries.Count == 1)
{
SearchResultEntryCollection entries = searchresults.Entries;
SearchResultEntry thispatron = entries[0];
PatronDN = thispatron.DistinguishedName;
}
}
If you wanted to move disabled users to a specific group, from this point you could write logic to check the DistinguishedName of that user, and throw a handled exception if their DistinguishedName contains the name of that group. Also, if the nsAccountLock attribute is available to your binding account as a readable attribute, you could just check the value of that attribute for true, and handle the user accordingly.
Here is the java code for disabling and enabling user in Active Directory using JNDI.
Make sure to connect with your AD before calling below code.
public void disableEnableUser() throws Exception {
ModificationItem[] mods = new ModificationItem[1];
//To enable user
//int UF_ACCOUNT_ENABLE = 0x0001;
//mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(UF_ACCOUNT_ENABLE)));
// To disable user
int UF_ACCOUNT_DISABLE = 0x0002;
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(UF_ACCOUNT_DISABLE)));
ctx.modifyAttributes("CN=John ABC,OU=Users,OU=anyone,DC=yourcompanyname,DC=com", mods);
}
Distinguished name = "CN=John ABC,OU=Users,OU=anyone,DC=yourcompanyname,DC=com"
This name is depend on your structure of Active Directory, you can confirm from your suport team.
If the directory software supports a password policy feature, it probably provides attributes to lock/deactivate the user. If not, you can simply nullify the password attribute (e.g., userpassword). The LDAP server should return the "inappropriate authentication" error to the client when the authenticated bind is performed.
You could just change the user's password. If it's OpenLDAP with the password-policy overlay, or another LDAP server that supports locking, you can lock the user as well. You really will have to find out.

Resources