We are creating an app that is meant to be used with a Service Account in your system; another user (user-2) has authorized this app by adding our app key to their Custom Application list. How do I get this User-2's UserID, so we can impersonate him and access his files list and files, etc. We need their UserID, so we can pass the "AS-User: " Header. And can this header be set using some property from within the .NET SDK - a sample code will be appreciated.
This does it for all enterprise users but you can easily put an if statement to get the user you're looking for.
static async Task MainAsync()
{
// rename the private_key.pem.example to private_key.pem and put your JWT private key in the file
var privateKey = File.ReadAllText(PRIVATE_KEY_FILE);
var boxConfig = new BoxConfig(CLIENT_ID, CLIENT_SECRET, ENTERPRISE_ID, privateKey, JWT_PRIVATE_KEY_PASSWORD, JWT_PUBLIC_KEY_ID);
var boxJWT = new BoxJWTAuth(boxConfig);
var adminToken = boxJWT.AdminToken();
Console.WriteLine("Admin Token: " + adminToken);
Console.WriteLine();
var adminClient = boxJWT.AdminClient(adminToken); // adminClient == serviceAccount
var userDetails = await adminClient.UsersManager.GetCurrentUserInformationAsync();
Console.WriteLine("\tAdmin User Details:");
Console.WriteLine("\tId: {0}", userDetails.Id);
Console.WriteLine("\tName: {0}", userDetails.Name);
Console.WriteLine("\tStatus: {0}", userDetails.Status);
Console.WriteLine();
var users = await adminClient.UsersManager.GetEnterpriseUsersAsync();
users.Entries.ForEach(i =>
{
Console.WriteLine("\t{0}", i.Name);
Console.WriteLine("\t{0}", i.Status);
if (i.Status == "active")
{
var userToken = boxJWT.UserToken(i.Id);
var userClient = boxJWT.UserClient(userToken, i.Id);
Task u = getUserItems(userClient, i.Id);
u.Wait();
}
});
}
static async Task getUserItems(BoxClient userClient, string id)
{
var userDetails = await userClient.UsersManager.GetCurrentUserInformationAsync();
Console.WriteLine("\nManaged User Details:");
Console.WriteLine("\tId: {0}", userDetails.Id);
Console.WriteLine("\tName: {0}", userDetails.Name);
Console.WriteLine("\tStatus: {0}", userDetails.Status);
Console.WriteLine();
Console.WriteLine("managed users older items");
var items = await userClient.FoldersManager.GetFolderItemsAsync("0", 500);
items.Entries.ForEach(i =>
{
Console.WriteLine("\t{0}", i.Name);
});
Console.WriteLine();
}
Related
HI I am trying to use Microsoft graph api to send messages.
Previously, I was sending messages/emails with the graph api without attachment. Now I need to attach 10 attachment each.
So I looked for examples and got to the Microsoft document and it shows the following code
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var attachment = new FileAttachment
{
Name = "smile",
ContentBytes = Convert.FromBase64String("R0lGODdhEAYEAA7")
};
await graphClient.Me.Messages["{message-id}"].Attachments
.Request()
.AddAsync(attachment);
Link:
https://learn.microsoft.com/en-us/graph/api/message-post-attachments?view=graph-rest-1.0&tabs=csharp
My question is what it is showing is not clear I am not sure I would I use message-id. Also I dont see if the Message is created and how the attachment is created.
Can someone help please.
You may refer to this document to learn the example about how to send email with attachments. And the below is my test code, it worked for me, I used client credential flow to provide authentication..
using Azure.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
public class HomeController : Controller
{
private readonly IWebHostEnvironment _appEnvironment;
public HomeController(IWebHostEnvironment appEnvironment)
{
_appEnvironment = appEnvironment;
}
public async Task<string> sendMailAsync() {
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_clientid";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var a = _appEnvironment.WebRootPath;//I have a file stored in my project
var file = a + "\\hellow.txt";
byte[] fileArray = System.IO.File.ReadAllBytes(#file);
//string base64string = Convert.ToBase64String(fileArray);
var message = new Message
{
Subject = "Meet for lunch?",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = "The new cafeteria is open."
},
ToRecipients = new List<Recipient>()
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "xxx#outlook.com"
}
}
},
Attachments = new MessageAttachmentsCollectionPage()
{
new FileAttachment
{
Name = "attachment.txt",
ContentType = "text/plain",
ContentBytes = fileArray
}
}
};
await graphClient.Users["user_id"]
.SendMail(message, null)
.Request()
.PostAsync();
return "success";
}
}
I add roles via the following code in the server project
foreach (var userRole in userInfo.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
}
var token = new JwtSecurityToken(
_config["JWTSettings:validIssuer"],
_config["JWTSettings:validAudience"],
claims,
null,
expires: DateTime.Now.AddMinutes(20),
signingCredentials: credentials);
Now, If I only have one role then this works fine and the following code in the OnInitializedAsync method in a razor component
var t = await AuthState;
var role1= t.User.IsInRole("admin");
leads to role1 being true.
However if I have multiple roles then role1 is then false (as it is for all the roles I add to the user) despite clearly being there!
Now, if I do the following in OnInitializedAsync
var t= await AuthState;
var claimsList= t.User.Claims;
foreach(var item in claimsList)
{
var s1 = item.Type;
var s2 = item.Value;
string asasas = string.Empty;
}
I get a single claim that has a type of role and it has the following as its value
["admin","myrole2"]
on the server side I get a number of claims of type role, each with a single role as the value.
What on earth is going on?
You need to transform your claims:
public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
ClaimsIdentity claimsIdentity = (ClaimsIdentity)user.Identity;
if (account is not null) {
MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
}
return user;
}
private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
{
foreach (var keyValuePair in account.AdditionalProperties) {
var key = keyValuePair.Key;
var value = keyValuePair.Value;
if (value is not null &&
value is JsonElement element && element.ValueKind == JsonValueKind.Array) {
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(keyValuePair.Key));
var claims = element.EnumerateArray()
.Select(x => new Claim(keyValuePair.Key, x.ToString()));
claimsIdentity.AddClaims(claims);
}
}
}
}
Program.cs in your client.
services.AddApiAuthorization().AddAccountClaimsPrincipalFactory<CustomUserFactory>();
I am trying to create O365 Group using Graph API, though I am able to get users and user profile details, I am unable to create group. My Azure tenant does have permission to create users/groups in the default directory and I did gave Profile and GroupReadWriteAll permissions.
static async Task CreateO365Group(string o365Group)
{
try
{
//Get the Graph client
var graphClient = AuthenticationHelper.GetAuthenticatedClient();
var token = await AuthenticationHelper.GetTokenForUserAsync();
var request = new HttpRequestMessage(HttpMethod.Post, graphClient.BaseUrl + "/groups");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var group = new Group
{
Description = "Group for Partners",
DisplayName = "O365Partners",
GroupTypes = new List<String>()
{
"Unified"
},
MailEnabled = true,
MailNickname = "library",
SecurityEnabled = false
};
await graphClient.Groups.Request().AddAsync(group);
var response = await graphClient.HttpProvider.SendAsync(request);
var bodyContents = await response.Content.ReadAsStringAsync();
Debug.WriteLine(bodyContents);
return bodyContents;
}
catch (Exception e)
{
Debug.WriteLine("Could not create Group {0} {1}", e.Message,o365Group);
return null;
}
}
I have been using facebook login for one of my asp.net core projects. However, it stopped working for Facebook login suddenly. I am getting HTTP 500 error.
The issue is, even in the debug more, asp.net core is not mentioning any error. It is just same 500 error code. Nothing else.
When I tried to set breakpoint in the first line of public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null) function, I realized that it is not even hitting that and failing even before that.
I am not sure how to debug this further. Am I missing anything here? Or is there any change from FB in the login side?
The return URL being hit is by Facebook is:
https://localhost:44300/signin-facebook?code=AQBxGGw7ZCoa9xtXc3CCsVGRD9TJLL428bZ_eJpUu4CtVu3K4UrfOZuYYdwFBXzGZ6GOGXpOi2Nme_jfbewB84otVZhKZfs4i7Dhi9Y3E_rloU9ouLeIvuOsm29jr7IDCtTj_HM7rKuKjj3zmc4yz5i_fniZ9ZhMfXtSus5KyKa4EFkZTsmKrz2ngMlGQalUAob_52GJNhvSIXDlmiNSrZLJV3m7Zbkf9eXETQkqhu2L1kgXPvWkMzVP8EN00GwRCYB3xT1kQMOimDANRKhziZjoVS5QZFUJTP0Faj47tE1xNfmAzb30iuwcaRORCOTMipUrnRvOO4nGRo8JuUNdPJaO&state=CfDJ8EHIO3qHMHFClr5BAt4EC1Wj7LyAs5Pg1XOqKo4uFiJM2Jr1rNyooxLIu2fbXr6Z3X5_kqbF_7WwFfvF3L3H4xgyooo-3Y9BV8Zh1S5wXlLJDAyCT5_LwkPJ1j8Zrwx4umQJp6NOl76GwRXpi1_BHlWGRxnh_naTL35iqeGovOa8oEDC0jOQ4trRe7YG3fV_ptjWk4yOnvJnsI81O-6wfyhdc3jm-LTP7ZO7-duf_lPZXZ8mL42XyLXDTIyOJ__S2yLYdvwItdDVntsM8Hwq94goXdU-RaH7ZkDA8iAzeCl3Ke0tWAdYBKy9vooJIXmE9Q#_=_
Based on this article, it should have state_token too in the URL. But that seems to be missing here. How can I figure out here what is the actual error?
I am using asp.net core RC2 release.
My callback function is:
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email);
if (email == null)
{
return View("Error");
}
/* Determine user from external login info */
var name = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Name);
string firstName;
string lastName = "";
if (!string.IsNullOrWhiteSpace(name))
{
firstName = name.Split(' ').Length > 1? name.Split(new[] { ' ' }, 2)[0] : name;
lastName = name.Split(' ').Length > 1 ? name.Split(new[] { ' ' }, 2)[1] : "";
}
else
firstName = email.Split('#')[0];
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
/* No user with same email ID. So, create a new user.*/
var newUser = new ApplicationUser
{
UserName = email,
Email = email,
FirstName = firstName,
LastName = lastName,
PasswordLastModifiedTime = DateTime.UtcNow,
UserSignUpDate = DateTime.UtcNow
};
var userCreationResult = await _userManager.CreateAsync(newUser);
if (userCreationResult.Succeeded)
{
userCreationResult = await _userManager.AddLoginAsync(newUser, info);
if (userCreationResult.Succeeded)
{
// Add user claims TODO:// Test if the claims are added successfully.
await _userManager.AddClaimAsync(newUser, new Claim("FirstName", newUser.FirstName));
await _userManager.AddClaimAsync(newUser, new Claim("LastName", newUser.LastName));
// Set user email to confirmed. This is more of work around
var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
userCreationResult = await _userManager.ConfirmEmailAsync(newUser, code);
if (userCreationResult.Succeeded)
{
//Create Subscription for user
var planService = new PlanServices();
var plan = planService.Find((int)SubscriptionType.Basic);
await _subscriptionService.CreateSubscription(newUser, plan, null);
await _signInManager.SignInAsync(newUser, isPersistent: false);
_logger.LogInformation(6, "User created an account using {Name} provider.",
info.LoginProvider);
await _emailSender.SendWelcomeEmailAsync(newUser.Email, newUser.FirstName);
return RedirectToLocal(returnUrl);
}
}
}
}
else
{
/* A user with email ID exists. Associate the account with that.*/
var loginAddResult = await _userManager.AddLoginAsync(user, info);
if (loginAddResult.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
}
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email, FirstName = firstName, LastName = lastName});
}
}
And ConfigureServices method is:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddScoped<ApplicationDbContext>();
services.AddIdentity<ApplicationUser, IdentityRole>(o =>
{
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonLetterOrDigit = false;
o.Password.RequiredLength = 8;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddCaching();
services.AddSession();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ISubscriptionService, SubscriptionService>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.Configure<RecaptchaOptions>(Configuration);
__serviceProvider = services.BuildServiceProvider();
}
The thing is, it worked well for a long time and has stopped working now. Also, it is not even hitting ExternalLoginCallback, so I am not sure where to head for debugging it further.
I am having issue in confirming new user email. the Confirm email link works for first 20 minutes , but after 50 minutes the link expires. I have set the token expiration time to 24 hours. Please help me in resolving this issue. I am stuck on it for last 2 days:(.My code is as follows:
I am setting the token lifetime in Create() method in ApplicationUserManager as following:
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = _settings.ConfirmationAndResetTokenExpirationTimeSpan
};
}
And then In AccountsController, the Create method for new user is geiven below. The SendEmailAsync method consist of email subject, email body, generated password and the callback uri.
[Authorize(Roles = Roles.Bam.Name.Admin)]
[HttpPost]
[Route(Routes.Accounts.Template.Create, Name = Routes.Accounts.Name.Create)]
public async Task<IHttpActionResult> Create(CreateUserBindingModel createUserBindingModel)
{
IHttpActionResult result;
var memberNameExists = UserManager.Users.Any(x => x.MemberName.ToLower() == createUserBindingModel.MemberName.ToLower());
if (!memberNameExists)
{
var applicationUser = new ApplicationUser
{
UserName = createUserBindingModel.Email,
Email = createUserBindingModel.Email,
FirstName = createUserBindingModel.FirstName,
LastName = createUserBindingModel.LastName,
Company = createUserBindingModel.Company,
Location = createUserBindingModel.Location,
PhoneNumber = createUserBindingModel.PhoneNumber,
MemberName = createUserBindingModel.MemberName,
LastLoginDate = SqlDateTime.MinValue.Value,
CreateDate = DateTime.Now,
CreatedBy = User.Identity.GetUserId(),
UpdateDate = DateTime.Now,
UpdatedBy = User.Identity.GetUserId(),
TwoFactorEnabled = createUserBindingModel.TwoFactorEnabled,
SecurityResetRequired = true,
PasswordExpirationDate = DateTime.Now.AddDays(Convert.ToDouble(ConfigurationManager.AppSettings["PasswordExpirationDays"]))
};
if (!string.IsNullOrEmpty(createUserBindingModel.AvatarBase64))
{
var avatarBytes = Convert.FromBase64String(createUserBindingModel.AvatarBase64);
var resizedAvatarBytes = ImageResizer.ResizeImage(avatarBytes, _avatarWidth, _avatarHeight);
applicationUser.UserAvatar = new ApplicationUserAvatar
{
Avatar = resizedAvatarBytes
};
}
var generatedPassword = PasswordGenerator.GenerateStrongPassword(10, 10);
var identityResult = await UserManager.CreateAsync(applicationUser, generatedPassword);
if (identityResult.Succeeded)
{
await UserManager.AddToRolesAsync(applicationUser.Id, createUserBindingModel.Roles.ToArray());
var token = await UserManager.GenerateEmailConfirmationTokenAsync(applicationUser.Id);
var callbackUri = string.Format("{0}?userId={1}&token={2}", createUserBindingModel.EmailConfirmationCallbackUri, applicationUser.Id, HttpUtility.UrlEncode(token));
await UserManager.SendEmailAsync(applicationUser.Id, Email.Confirmation.Subject, string.Format(Email.Confirmation.Body, string.Format("{0} {1}", applicationUser.FirstName, applicationUser.LastName), callbackUri, generatedPassword, _settings.AccessTokenExpirationTimeSpan.TotalHours));
var userUrl = new Uri(Url.Link(Routes.Accounts.Name.Get, new { id = applicationUser.Id }));
var roles = await UserManager.GetRolesAsync(applicationUser.Id);
var contract = _accountsMapper.ToContract(applicationUser, roles);
result = Created(userUrl, contract);
}
else
{
result = GetErrorResult(identityResult);
}
}
else
{
ModelState.AddModelError(string.Empty, "Member Name already exists!");
result = BadRequest(ModelState);
}
return result;
}
Once the email is generated the UI has following JS angular code which gets executed and the provide the userid and token to service.
Angular JS code:
angular.module('confirmEmailModule').factory('confirmEmailFactory', function ($http) {
var factory = {};
factory.confirmEmail = function(userId, token) {
var encodedToken = encodeURIComponent(token);
var uri = '/identity/api/accounts/confirmemail?userId=' + userId + '&token=' + token;
return $http.post(uri);
}
return factory;
});
and the Service is :
[AllowAnonymous]
[HttpPost]
[Route(Routes.Accounts.Template.ConfirmEmail, Name = Routes.Accounts.Name.ConfirmEmail)]
public async Task<IHttpActionResult> ConfirmEmail([FromUri] string userId, [FromUri] string token)
{
//var decodedToken = HttpUtility.UrlDecode(token);
var identityResult = await UserManager.ConfirmEmailAsync(userId, token);
var result = identityResult.Succeeded ? StatusCode(HttpStatusCode.NoContent) : GetErrorResult(identityResult);
return result;
}
Please advice.
I found the solution to this issue. I am posting it if somebody faced the same issue. In my case the services and web API were on different servers. Different machine keys caused this issue. So I generated the machine key for my Web application and posted the same machine key in web.config file of Identity service. After that it worked. For more information on generating machine key, following link is helpful.
http://gunaatita.com/Blog/How-to-Generate-Machine-Key-using-IIS/1058
This is what worked for me. Hope it helps out;
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return RedirectToAction("employees", "home");
}
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"The User ID {userId} is invalid";
return View("NotFound");
}
var result = await userManager.ConfirmEmailAsync(user, Uri.EscapeDataString(token));
if (result != null)
{
user.EmailConfirmed = true;
await userManager.UpdateAsync(user);
return View();
}
}