Create AWS Cognito user with account status "CONFIRMED" and without email address - c#

How can I create a Cognito user with the account status confirmed using c#? After a user is created the account status displays FORCE_CHANGE_PASSWORD. Another thing is I need to create user without email address.
AmazonCognitoIdentityProviderClient cognitoProvider =
new AmazonCognitoIdentityProviderClient(region);
string userName = "user";
string tempPassword = "Temp#3434";
string newPassword = "RealPass#2019";
AdminCreateUserRequest adminUserCreateRequest = new AdminCreateUserRequest()
{
UserPoolId = poolId,
Username = userName,
TemporaryPassword = tempPassword
};
AdminCreateUserResponse signUpResponse = await cognitoProvider.AdminCreateUserAsync(adminUserCreateRequest);
Admin InitiateRequest
Dictionary<string, string> initialParams = new Dictionary<string, string>();
initialParams.Add("USERNAME", userName);
initialParams.Add("PASSWORD", tempPassword);
AdminInitiateAuthRequest initialRequest = new AdminInitiateAuthRequest()
{
AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH,
AuthParameters = initialParams,
ClientId = appClientId_tenantApi,
UserPoolId = poolId
};
AdminInitiateAuthResponse resInitAuth = await cognitoProvider.AdminInitiateAuthAsync(initialRequest);
InitiateAuthRresponse has email as a required attribute.
{[requiredAttributes, ["userAttributes.email"]]}
But the documentation doesn't say so.
For ADMIN_NO_SRP_AUTH: USERNAME (required), SECRET_HASH (if app client is configured with client secret), PASSWORD (required), DEVICE_KEY
Admin Respond to challenge
var authParameters = new Dictionary<string, string>();
authParameters.Add("USERNAME", userName);
authParameters.Add("NEW_PASSWORD", newPassword);
AdminRespondToAuthChallengeRequest adminAuthRequest = new AdminRespondToAuthChallengeRequest()
{
UserPoolId = poolId,
ClientId = appClientId_tenantApi,
ChallengeName = ChallengeNameType.NEW_PASSWORD_REQUIRED,
ChallengeResponses = authParameters,
Session = session
};
cognitoProvider.AdminRespondToAuthChallengeAsync(adminAuthRequest);
I am thinking I may missed some user settings in Cognito to avoid email. Any one have similar experience ? or is this not possible to create user without email ?

During the creation of the user pool, under general settings;attributes as in the photocognito creation on aws one is required to choose the attributes that must be present, i believe in your case the email was selected by default hence the challenge request response you got.
The admin create user request requires the client to confirm the email for purposes of verification that the user owns the email.
A hack for the same would be to allow users to sign themselves up on your cognito configuration, then sign someone up then follow with a username and password, then proceed to confirm them as an admin
var signup = await cognitoClient.SignUpAsync(new SignUpRequest
{
Username = person.Username,
ClientId = cognitoOptions.ClientId,
Password = person.IdNumber,
});
var confirm = await cognitoClient.AdminConfirmSignUpAsync(new AdminConfirmSignUpRequest
{
Username = person.Username,
UserPoolId = cognitoOptions.UserPoolId
});

In case if anyone still looking for answer
Initalize Provider.
AmazonCognitoIdentityProviderClient provider = new AmazonCognitoIdentityProviderClient("*************", "************", Amazon.RegionEndpoint.USWest);
Create user
AdminCreateUserResponse adminCreateUserResponse = await provider.AdminCreateUserAsync(new AdminCreateUserRequest
{
Username = "TestUser",
TemporaryPassword = "TempPassword#1",
UserPoolId = "us-west-**********"
});
Authenticate user
CognitoUserPool userPool = new CognitoUserPool("us-west-***", "***", provider);
CognitoUser user = new CognitoUser("TestUser", "******", userPool, provider, "**********");
InitiateSrpAuthRequest authRequest = new InitiateSrpAuthRequest()
{
Password = "TempPassword#1"
};
AuthFlowResponse authResponse = await user.StartWithSrpAuthAsync(authRequest).ConfigureAwait(false);
Vaidate user authentication result and get the user AccessToken
if (authResponse.AuthenticationResult == null)
{
if (authResponse.ChallengeName == ChallengeNameType.NEW_PASSWORD_REQUIRED)
{
//Console.WriteLine("Enter your desired new password:");
string newPassword = "NewPWD#1";// Console.ReadLine();
Dictionary<string, string> att = new Dictionary<string, string>();
att.Add("userAttributes.email", "testemail#xyz.com");
user.Attributes.Add("preferred_username", "TestUser1");
And update the new password using Accesstoken ( post update the User status will be confirmed)
authResponse = await user.RespondToNewPasswordRequiredAsync(new RespondToNewPasswordRequiredRequest()
{
SessionID = authResponse.SessionID,
NewPassword = newPassword,
},att);
accessToken = authResponse.AuthenticationResult.AccessToken;
}

Related

Send email using Graph API from service account

I am working on task in ASP.NET Core 5 (C#) which requires to send an email using Graph API, I have referred to following article and did the configuration on the Azure trial account and was able to send the emails.
Sending e-mails with Microsoft Graph using .NET
This is the send email code:
//send email
var client = await GetAuthenticatedGraphClient();
await client.Users[senderObjectId]
.SendMail(graphMailMessage, true)
.Request()
.PostAsync();
senderObjectId - Object Id coming from config
We deployed the same code on the client's Azure account we needed the User Object Id for the service account that we are going to use as a sender's email id. However, the client came back saying that the account is not part of the Azure AD and its a service account. Is there a way of sending emails without using the user object id.
Here's the method which takes parameters for mail sending. Also, it separates (comma) the mail and sends it to multiple users
public string SendEmail(string fromAddress, string toAddress, string CcAddress, string subject, string message, string tenanatID , string clientID , string clientSecret)
{
try
{
var credentials = new ClientSecretCredential(
tenanatID, clientID, clientSecret,
new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud });
GraphServiceClient graphServiceClient = new GraphServiceClient(credentials);
string[] toMail = toAddress.Split(',');
List<Recipient> toRecipients = new List<Recipient>();
int i = 0;
for (i = 0; i < toMail.Count(); i++)
{
Recipient toRecipient = new Recipient();
EmailAddress toEmailAddress = new EmailAddress();
toEmailAddress.Address = toMail[i];
toRecipient.EmailAddress = toEmailAddress;
toRecipients.Add(toRecipient);
}
List<Recipient> ccRecipients = new List<Recipient>();
if (!string.IsNullOrEmpty(CcAddress))
{
string[] ccMail = CcAddress.Split(',');
int j = 0;
for (j = 0; j < ccMail.Count(); j++)
{
Recipient ccRecipient = new Recipient();
EmailAddress ccEmailAddress = new EmailAddress();
ccEmailAddress.Address = ccMail[j];
ccRecipient.EmailAddress = ccEmailAddress;
ccRecipients.Add(ccRecipient);
}
}
var mailMessage = new Message
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = message
},
ToRecipients = toRecipients,
CcRecipients = ccRecipients
};
// Send mail as the given user.
graphServiceClient
.Users[fromAddress]
.SendMail(mailMessage, true)
.Request()
.PostAsync().Wait();
return "Email successfully sent.";
}
catch (Exception ex)
{
return "Send Email Failed.\r\n" + ex.Message;
}
}

Issue with Remove Application Role in AzureAD using GraphAPI / C#

I am trying to Add/Remove Application Role in AzureAD using VisualStudio/C#/GraphAPI. I can successfully add user to ApplicationRole but Remove(or Delete) role doesn't work.
I researched on internet and it seems an issue with AzureAD graph API itself. check:
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/5707763c-41f7-4465-abdb-3a8d8ded153b/graph-api-apiversion15-how-to-remove-user-from-application-role-using-c-net?forum=WindowsAzureAD
However, it's an old post so not sure if any workaround is available now.
Any help is appreciated to fix this issue.
I can successfully add user to ApplicationRole but Remove(or Delete) role doesn't work.
I can remove the application role with follow code.
var listRoles = user.AppRoleAssignments.ToList();
user.AppRoleAssignments.Remove(listRoles[0]); //just demo: you could remove the role as your wanted
user.UpdateAsync().Wait();
The following is my detail test demo code
1.get access token
private static async Task<string> GetAppTokenAsync(string graphResourceId, string tenantId, string clientId, string secretKey)
{
string aadInstance = "https://login.microsoftonline.com/" + tenantId + "/oauth2/token";
AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance, false);
var result = await authenticationContext.AcquireTokenAsync(graphResourceId,
new ClientCredential(clientId, userId));
return result.AccessToken;
}
2.Init the graphclient.
var graphResourceId = "https://graph.windows.net";
var tenantId = "tenantId";
var clientId = "client Id";
var secretKey = "secret key";
var servicePointUri = new Uri(graphResourceId);
var serviceRoot = new Uri(servicePointUri, tenantId);
var activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await GetAppTokenAsync(graphResourceId, tenantId, clientId, secretKey));
3.create application and service principal
Application appObject = new Application { DisplayName = "Test-Demo App" };
appObject.IdentifierUris.Add("https://localhost/demo/" + Guid.NewGuid());
appObject.ReplyUrls.Add("https://localhost/demo");
AppRole appRole = new AppRole
{
Id = Guid.NewGuid(),
IsEnabled = true,
DisplayName = "Something",
Description = "Anything",
Value = "policy.write"
};
appRole.AllowedMemberTypes.Add("User");
appObject.AppRoles.Add(appRole);
activeDirectoryClient.Applications.AddApplicationAsync(appObject).Wait();
// create a new Service principal
ServicePrincipal newServicePrincpal = new ServicePrincipal
{
DisplayName = appObject.DisplayName,
AccountEnabled = true,
AppId = appObject.AppId
};
activeDirectoryClient.ServicePrincipals.AddServicePrincipalAsync(newServicePrincpal).Wait();
4.add role assginments
User user = (User) activeDirectoryClient.Users.GetByObjectId("userobjectId").ExecuteAsync().Result;
AppRoleAssignment appRoleAssignment = new AppRoleAssignment
{
Id = appRole.Id,
ResourceId = Guid.Parse(newServicePrincpal.ObjectId),
PrincipalType = "User",
PrincipalId = Guid.Parse(user.ObjectId),
};
user.AppRoleAssignments.Add(appRoleAssignment);
user.UpdateAsync().Wait();
5.remove the role from user
var listRoles = user.AppRoleAssignments.ToList();
user.AppRoleAssignments.Remove(listRoles[0]);
user.UpdateAsync().Wait();

Update Umbraco user's user type, based on the roles from external identity

In the last few days I have been working on integrating Umbraco Backoffice with IdentityServer v3. I have managed to get to the point, where I authenticate user externally and have Umbraco create a user with some default user type in the backoffice and link it to the external account.
The next thing I'm doing is updating the Umbraco user type, based on the roles of the user. I think I found a way of doing that on linking the Umbraco to the external account, but I cannot see any way to constantly update the user types with each login, in case the roles were removed/added for a user.
By analyzing the code in Umbraco BackOfficeController, it seems there is no way to get into the process of authenticating and update data on the side of Umbraco.
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
else
{
if (await AutoLinkAndSignInExternalAccount(loginInfo) == false)
{
ViewBag.ExternalSignInError = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to to an account" };
}
}
It seems that if the umbraco login is found, then the user is just being logged in, without any exposed events or options. Only if the user is not found, then the whole process of creation and linking is started, where I could actually make some changes to the user properties.
That said, is there any way to actually update the user types of an Umbraco user, based on the claims from external server, on every login?
My code from the Startup class is below.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
var idAuth = new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44332",
ClientId = "id",
ClientSecret = "secret",
RedirectUri = "http://localhost:8081/Umbraco",
ResponseType = "id_token token",
Scope = "openid profile roles email",
Caption = "test",
SignInAsAuthenticationType = Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType
};
idAuth.Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var id = n.AuthenticationTicket.Identity;
var givenName = id.FindFirst(System.Security.Claims.ClaimTypes.GivenName);
var familyName = id.FindFirst(System.Security.Claims.ClaimTypes.Surname);
var roles = id.FindAll(System.Security.Claims.ClaimTypes.Role);
var nid = new ClaimsIdentity(
id.AuthenticationType,
System.Security.Claims.ClaimTypes.GivenName,
System.Security.Claims.ClaimTypes.Role);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaims(roles);
nid.AddClaim(id.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier));
nid.AddClaim(id.FindFirst(System.Security.Claims.ClaimTypes.Email));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
}
};
//idAuth.AuthenticationType = "https://localhost:44332";
idAuth.ForUmbracoBackOffice("btn-google-plus", "fa-google-plus"); //temporary icon/button
idAuth.AuthenticationType = "https://localhost:44332";
var externalOptions = new ExternalSignInAutoLinkOptions(autoLinkExternalAccount: true, defaultUserType: "admin");
//externalOptions.OnAutoLinking; // TODO: set user type based on roles
idAuth.SetExternalSignInAutoLinkOptions(externalOptions);
app.UseOpenIdConnectAuthentication(idAuth);
Managed to solve this some time ago by manually checking the roles claim and Umbraco UserType on SecurityTokenValidated with the help of Umbraco services IExternalLoginService and IUserService. If the combination is not right (e.g. the administrator role is not present in the claim), I use Umbraco IUserService to update that user's UserType
Notifications =
new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async n =>
{
var id = n.AuthenticationTicket.Identity;
var uid = id.FindFirst(ClaimTypes.NameIdentifier);
var givenName = id.FindFirst(ClaimTypes.GivenName);
var familyName = id.FindFirst(ClaimTypes.Surname);
var roles = id.FindAll(ClaimTypes.Role);
var rolesList = roles as IList<Claim> ?? roles.ToList();
if (
!rolesList.Any(
c =>
string.Equals(c.Value, RoleNames.ContentEditor,
StringComparison.InvariantCultureIgnoreCase)))
throw new HttpException(403,
"You do not have any roles configured for the application");
// create new identity and set name and role claim type
var nid = new ClaimsIdentity(
id.AuthenticationType,
ClaimTypes.GivenName,
ClaimTypes.Role);
UpdateUserType(uid.Value, rolesList, applicationConfiguration.AuthorityUrl);
nid.AddClaim(givenName);
nid.AddClaim(familyName);
nid.AddClaims(rolesList);
nid.AddClaim(uid);
nid.AddClaim(id.FindFirst(ClaimTypes.Email));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
}
}
private static void UpdateUserType(string uid, IList<Claim> roles, string providerName)
{
var userService = ApplicationContext.Current.Services.UserService;
var oneUser = ApplicationContext.Current.Services.ExternalLoginService.Find(new UserLoginInfo(providerName, uid)).FirstOrDefault();
if (oneUser == null)
return;
var user = userService.GetUserById(oneUser.UserId);
if (user == null)
return;
if (
roles.Any(
r => string.Equals(r.Value, RoleNames.Administrator, StringComparison.InvariantCultureIgnoreCase))
&& !string.Equals(user.UserType.Alias, UmbracoRoleNames.Administrator))
{
SetUserType(user, UmbracoRoleNames.Administrator, userService);
return;
}
if (
roles.Any(
r => string.Equals(r.Value, RoleNames.ContentEditor, StringComparison.InvariantCultureIgnoreCase))
&& !string.Equals(user.UserType.Alias, UmbracoRoleNames.ContentEditor))
{
SetUserType(user, UmbracoRoleNames.ContentEditor, userService);
return;
}
}
private static void SetUserType(Umbraco.Core.Models.Membership.IUser user, string alias, IUserService userService)
{
try
{
user.UserType = userService.GetUserTypeByAlias(alias);
userService.Save(user);
}
catch (Exception e)
{
LogHelper.Error(typeof(ClassName), "Could not update the UserType of a user.", e);
}
}
In this specific case, I do not change the UserType back to a non-admin/non-content editor one when someone lacks that privilege from their roles claim, because they are being filtered out one step before and a 403 error code is being returned.

How to avoid google API access code or access token by C#

I'm developing an application to insert row to one of my spreadsheet (like database). The problem is, it always asking new access code. Please help me to avoid expiring the access code or I just want to update my google drive account excel. Sometimes there will be a easy code, may I using wrong code? Below is my code:
string CLIENT_ID = "HIDE";
string CLIENT_SECRET = "HIDE";
string SCOPE = "https://spreadsheets.google.com/feeds https://docs.google.com/feeds";
string REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.ClientId = CLIENT_ID;
parameters.ClientSecret = CLIENT_SECRET;
parameters.RedirectUri = REDIRECT_URI;
parameters.Scope = SCOPE;
string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
auth.Text = authorizationUrl;
info.Text = ("Please visit the URL above to authorize your OAuth " +"request token. Once that is complete, type in your access code to "+ "continue...");
parameters.AccessCode = accc.Text;
OAuthUtil.GetAccessToken(parameters);
string accessToken = parameters.AccessToken;
acc2.Text = accessToken;
GOAuth2RequestFactory requestFactory =
new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
string USERNAME = "HIDE";
string PASSWORD = "HIDE";
service.setUserCredentials(USERNAME, PASSWORD);
SpreadsheetQuery query = new SpreadsheetQuery();
SpreadsheetFeed feed = service.Query(query);
if (feed.Entries.Count == 0)
{
Console.WriteLine("None");
}
SpreadsheetEntry spreadsheet = (SpreadsheetEntry)feed.Entries[0];
Console.WriteLine(spreadsheet.Title.Text);
WorksheetFeed wsFeed = spreadsheet.Worksheets;
WorksheetEntry worksheet = (WorksheetEntry)wsFeed.Entries[0];
AtomLink listFeedLink = worksheet.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);
ListQuery listQuery = new ListQuery(listFeedLink.HRef.ToString());
ListFeed listFeed = service.Query(listQuery);
ListEntry row = new ListEntry();
row.Elements.Add(new ListEntry.Custom() { LocalName = "no.", Value = "#" });
service.Insert(listFeed, row);
You can make a request for "offline" access when requesting the access token. The server will return a "refresh token" and with this toke you can request a new "access token" when it expires without having the user to grant permissions again.
Here you can find documentation on that.
Check this tutorial, it will help you to obtain the access token and refresh token programmatically instead of having to open the browser and copy the token.

Authorize user by google account in desktop application without webauthorizationBroker

Google has new api Google.Apis.
I have desktop application which authorize user by google account. Old API stop working.
How can I authorize user by new API, but WITHOUT webauthorizationBroker in C#? I do not want open webbroswer, I have own win form for enter login and password.
EDITED:
Here is old code:
string fullname = string.Empty;
UserService userService = new UserService("APPKEY");
userService.setUserCredentials(username, Encryptor.Decrypt(password));
try
{
string token = userService.QueryClientLoginToken();
GOAuthRequestFactory reqF = new GOAuthRequestFactory("apps", "APPKEY") { ConsumerKey = "KEY", ConsumerSecret = "SECRET" };
userService = new UserService("APPKEY") { RequestFactory = reqF };
UserQuery query = new UserQuery("DOMAIN", true);
UserFeed usersFeed = userService.Query(query);
UserEntry entry = (UserEntry)usersFeed.Entries.FirstOrDefault(f => (f as UserEntry).Login.UserName == userName);
fullname = string.Format("{0} {1}", entry.Name.GivenName, entry.Name.FamilyName);
}
catch (InvalidCredentialsException ex)
{
service.SendError(ex.Message);
return fullname;
}
return fullname;

Categories