Active Directory authentication for Umbraco 7.6 gets stuck in a re-direct loop - azure

I am currently using an Umbraco library to extend the Authentication possibilities and enable back office authentication with Active Directory.
https://github.com/umbraco/UmbracoIdentityExtensions
After installing the library and following the blog post below, I was able to display an external login button, authenticate with Active Directory and add a user and external login to the Umbraco database.
https://www.jdibble.co.uk/blog/securing-umbraco-backoffice-with-azure-active-directory/
This then sends you back to the /umbraco login page in a continuous loop. As described by this blog post https://our.umbraco.org/forum/developers/extending-umbraco/75256-login-uisng-azure-ad-redirects-allways-to-login-page
Has anyone faced this issue and solved it? Or have any useful suggestions?
The code being used...
public static void ConfigureBackOfficeAzureActiveDirectoryAuth(this IAppBuilder app,
string tenant, string clientId, string postLoginRedirectUri, Guid issuerId,
string caption = "Active Directory", string style = "btn-microsoft", string icon = "fa-windows")
{
var authority = string.Format(
CultureInfo.InvariantCulture,
"https://login.microsoftonline.com/{0}",
tenant);
var adOptions = new OpenIdConnectAuthenticationOptions
{
SignInAsAuthenticationType = Constants.Security.BackOfficeExternalAuthenticationType,
ClientId = clientId,
Authority = authority,
RedirectUri = postLoginRedirectUri,
AuthenticationMode = AuthenticationMode.Passive,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async context =>
{
if (System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Break();
var userService = ApplicationContext.Current.Services.UserService;
var stuff = (List<Claim>)context.JwtSecurityToken.Claims;
var email = stuff.FirstOrDefault(x => x.Type == "unique_name").Value;
var issuer = stuff.FirstOrDefault(x => x.Type == "iss").Value;
var providerKey = stuff.FirstOrDefault(x => x.Type == "sub").Value;
var name = stuff.FirstOrDefault(x => x.Type == "name").Value;
var userManager = context.OwinContext.GetUserManager<BackOfficeUserManager>();
var user = userService.GetByEmail(email);
if (user == null)
{
var writerUserType = userService.GetUserTypeByName("writer");
user = userService.CreateUserWithIdentity(email, email, writerUserType);
}
var identity = await userManager.FindByEmailAsync(email);
if (identity.Logins.All(x => x.ProviderKey != providerKey))
{
identity.Logins.Add(new IdentityUserLogin(issuer, providerKey, user.Id));
identity.Name = name;
var result = userManager.Update(identity);
}
},
}
};
adOptions.ForUmbracoBackOffice(style, icon);
adOptions.Caption = caption;
//Need to set the auth type as the issuer path
adOptions.AuthenticationType = string.Format(
CultureInfo.InvariantCulture,
"https://sts.windows.net/{0}/",
issuerId);
adOptions.SetExternalSignInAutoLinkOptions(new ExternalSignInAutoLinkOptions(autoLinkExternalAccount: true));
app.UseOpenIdConnectAuthentication(adOptions);
}

Related

How to retrieve Claims Value in .Net Core 2.0

As it says in the title I have already assigned claims to the registered user, I am now trying to retrieve the claim value when the user logs into the application within the UserClaims table in sql server which I find a bit difficult to do as this is my first time using claims.
Looking for directions on our to achieve this, thank you in advance.
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.UserName, Email = model.Email, UserRoleId = model.RoleId };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.EmailConfirmationLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailConfirmationAsync(model.Email, callbackUrl);
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created a new account with password.");
await addUserClaims(model.CusomterId, model.UserName);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
List<UserRole> roles = _userRoleRepo.GetAll();
model.CreateRoleList(roles);
List<Customer> customers = await _customerRepository.GetAll();
model.SetupCustomerOptionList(customers);
// If we got this far, something failed, redisplay form
return View(model);
}
private async Task addUserClaims(string CustomerID ,string username)
{
// Customer customer = _customerRepository.GetById(customerid);
List<Customer> customers = await _customerRepository.GetAll();
Customer customer = _customerRepository.GetById(CustomerID);
var user = await _userManager.FindByNameAsync(username);
;
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Name, CustomerID));
}
Set
var claims = new List<Claim>
{
new Claim("Currency", "PKR")
};
Get
#User.Claims.FirstOrDefault(c => c.Type == "Currency").Value
Very easy
public static class IdentityExtension
{
public static string GetId(this IIdentity identity)
{
ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;
Claim claim = claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
return claim.Value;
}
}
Example
User.Identity.GetId();

Mobile App GetIdentityAsync no value

I have this code from Azure Services Mobile App, API custom controller. The result always the same "1:1". I test with all identity providers, google, facebook, twitter, and Microsoft Account, except AD. I think the problem is the call to GetIdentityAsync. Can anyone help me? Thanks
[HttpGet]
public async Task<string> GetIdentityInfo()
{
var user = (MobileAppUser)this.User;
string str = "";
string Provider = "YES", UserId = "NO";
try
{
if (user != null)
{
Provider = "1"; UserId = "1";
var microsoftCredentials = await user.GetIdentityAsync<MicrosoftAccountCredentials>();
if (microsoftCredentials != null && microsoftCredentials.Claims != null)
{
Provider = "MICROSOFT";
UserId = microsoftCredentials.UserId;
}
Provider = "2"; UserId = "2";
var googleCredentials = await user.GetIdentityAsync<GoogleCredentials>();
if (googleCredentials != null && googleCredentials.Claims != null)
{
Provider = "GOOGLE";
UserId = googleCredentials.UserId;
}
Provider = "3"; UserId = "3";
var facebookCredentials = await user.GetIdentityAsync<FacebookCredentials>();
if (facebookCredentials != null && facebookCredentials.Claims != null)
{
Provider = "FACEBOOK";
UserId = facebookCredentials.UserId;
}
Provider = "4"; UserId = "4";
var twitterCredentials = await user.GetIdentityAsync<TwitterCredentials>();
if (twitterCredentials != null && twitterCredentials.Claims != null)
{
Provider = "TWITTER";
UserId = twitterCredentials.UserId;
}
}
else
{
Provider = "NONE"; UserId = "NULL";
}
}
catch (Exception ex)
{
str = "ERROR";
}
finally
{
str = Provider + ":" + UserId;
}
return str;
}
We receive support from Microsoft Support Engineer Adrian Fernandez Garcia, and send an example that worked OK. The unique difference was that EMA_RuntimeUrl must be created manually in mobile app in azure portal in Application Properties, and assigned to Gateway address.
Actually this value is created automatically and don't have to create it manually.
This gave us the error URIFormat NULL value.
All is working OK now.
Thanks to Microsoft for the support.

Send email using Office 365 using unified API

We are trying to use the O365 Unified API to send emails from our line-of-business apps. I use the following code to send the email. This throws a DataServiceQueryException exception "Unauthorized".
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
await graphClient.Me.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
private static Message InitializeMessage(EmailMessage message)
{
ItemBody body = new ItemBody {Content = message.Body, ContentType = BodyType.HTML};
Message m = new Message
{
Body = body,
Subject = message.Subject,
Importance = Importance.Normal,
};
//Add all the to email ids
if (message.ToRecipients != null)
foreach (Models.Messaging.EmailAddress emailAddress in message.ToRecipients)
{
m.ToRecipients.Add(new Recipient { EmailAddress = new Microsoft.Graph.EmailAddress { Address = emailAddress.Address, Name = emailAddress.Name } });
}
return m;
}
The code for _authenticationHelper.GetGraphClientAsync() is
public async Task<GraphService> GetGraphClientAsync()
{
Uri serviceRoot = new Uri(appConfig.GraphResourceUriBeta + appConfig.Tenant);
_graphClient = new GraphService(serviceRoot,
async () => await AcquireTokenAsyncForUser(appConfig.GraphResourceUri, appConfig.Tenant));
return _graphClient;
}
private async Task<string> AcquireTokenAsyncForUser(string resource, string tenantId)
{
AuthenticationResult authenticationResult = await GetAccessToken(resource, tenantId);
_accessCode = authenticationResult.AccessToken;
return _accessCode;
}
private async Task<AuthenticationResult> GetAccessToken(string resource, string tenantId)
{
string authority = appConfig.Authority;
AuthenticationContext authenticationContext = new AuthenticationContext(authority);
ClientCredential credential = new ClientCredential(appConfig.ClientId, appConfig.ClientSecret);
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
string userAccessToken = authHeader.Substring(authHeader.LastIndexOf(' ')).Trim();
UserAssertion userAssertion = new UserAssertion(userAccessToken);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, credential, userAssertion);
return authenticationResult;
}
However if I change the SendEmailAsUserAsync method as shown below, the email is sent but an InvalidOperationException is thrown with message "The complex type 'System.Object' has no settable properties."
public async Task SendEmailAsUserAsync(EmailMessage message)
{
try
{
var graphClient = await _authenticationHelper.GetGraphClientAsync();
Message m = InitializeMessage(message);
//await graphClient.Me.SendMailAsync(m, true); //This did not work
var user = await graphClient.Me.ExecuteAsync();
await user.SendMailAsync(m, true);
}
catch (DataServiceQueryException dsqe)
{
_logger.Error("Could not get files: " + dsqe.InnerException.Message, dsqe);
throw;
}
}
Can any one point out if there is something wrong here.
Check the example project below, this has a working example (after you fill in the ClientID etc. in app.config).
Office 365 API demo applications
For sending email it uses the function below, which works if you set it up correctly. It also has a number of functions for Authenticating using Authorization Code Grant Flow.
public async Task SendMail(string to, string subject, string body)
{
var client = await this.AuthenticationHelper
.EnsureOutlookServicesClientCreatedAsync(
Office365Capabilities.Mail.ToString());
Message mail = new Message();
mail.ToRecipients.Add(new Recipient()
{
EmailAddress = new EmailAddress
{
Address = to,
}
});
mail.Subject = subject;
mail.Body = new ItemBody() { Content = body, ContentType = BodyType.HTML };
await client.Me.SendMailAsync(mail, true);
}
Actually, there is no assembly wrapper for the graph API.
Microsoft.Graph.dll is deprecrated.
So, you should to :
Deal with the REST requests : See here : http://graph.microsoft.io/docs/api-reference/v1.0/api/message_send
Generate a wrapper with Microsoft.Vipr project : see here : https://github.com/microsoft/vipr
For the authentication, ADAL works fine :)

Self-hosted Owin, static files and authentication

I need to embed a small Owin host in an Azure worker Role.
I need to serve some static files, but a sub-directory should be password-protected. I couldn't find any good example on self-hosted apps with authentication.
I'm trying to setup the following appBuilder:
Func<CookieValidateIdentityContext, Task> validate = async context =>
{
Logger.Trace("Access");
};
var authOptions = new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType =
DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login.html"),
Provider =
new CookieAuthenticationProvider
{
OnValidateIdentity
= validate,
OnApplyRedirect = redirect,
OnResponseSignIn = signIn
}
};
appBuilder.UseCookieAuthentication(authOptions);
appBuilder.UseStageMarker(PipelineStage.Authenticate);
var basePath = GetBasePath();
var webPath = Path.Combine(basePath, "Web");
var physicalFileSystem = new PhysicalFileSystem(webPath);
var options = new FileServerOptions
{
EnableDefaultFiles = true,
EnableDirectoryBrowsing = true,
FileSystem = physicalFileSystem
};
options.StaticFileOptions.FileSystem = physicalFileSystem;
options.StaticFileOptions.ServeUnknownFileTypes = true;
options.DefaultFilesOptions.DefaultFileNames = new[] { "index.html" };
appBuilder.UseFileServer(options);
I'm stuck with two problems:
The Logger.Trace("Access") breakpoint is never hit
How should I define the Login.html page? Should it contain a form? But how can I handle the post action?

CoSign API: SPML WS - User Management Query

I'm trying to create a new user using the CoSign SPML WS.
The code I'm using (C#.NET) is below. Can someone please let me know if the PSOIdentifierType.ID is correct (i.e. "The ID of the User") and what should the Target ID be, I think it should be blank as the user does not exist yet.
private const readonly string addTargetDB = "CoSignDB";
public void CreateBasicUser(string userName, string userLoginName, string userPassword, string userCN, string userEmail)
{
SPMLSoapClient client = new SPMLSoapClient();
AddRequestType request = new AddRequestType();
PSOIdentifierType psoCreationType = new PSOIdentifierType();
psoCreationType.ID = userName;
psoCreationType.targetID = ""; //The parameter that was returned in the ListTargets operation
UserRecord newUserRecord = new UserRecord();
newUserRecord.UserLoginName = userLoginName;
newUserRecord.Password = userPassword;
newUserRecord.UserCN = userCN;
newUserRecord.EmailAddress = userEmail;
newUserRecord.RightsMask = (uint)1; // 1 - User
newUserRecord.UserKind = UserKindEnum.User;
request.returnData = ReturnDataType.everything;
request.targetID = addTargetDB;
request.psoID = psoCreationType;
request.UserRecord = newUserRecord;
AddResponseType clientAddResponse = client.add(request);
if(clientAddResponse.status == StatusCodeType.success)
{
// OK
}
else if(clientAddResponse.status == StatusCodeType.failure)
{
// Fail
}
}
The id of the user is correct (it should be the username) and the TargetID should be "CoSignDB"

Resources