How to create a SalesForce 'User' with REST API? - salesforce

I am trying to create a User in SFDC with REST API on Java Client.
Please see my code below and help me to understand why I am getting the error message below:
HTTP Status 400 ::
{
errorCode: "NOT_FOUND"
message: "The requested resource does not exist"
}
CODE:
HttpClient httpClient = new HttpClient();
JSONObject user = new JSONObject();
String userId = null;
try{
user.put("Username", "email#domain.com");
user.put("Alias", "DemoAPI");
user.put("ProfileId", "00e90000000fKXB");
user.put("Email", "email123456#domain.com");
user.put("EmailEncodingKey", "ISO-8859-1");
user.put("LastName", "REST API Test");
user.put("LanguageLocaleKey", "pt_BR");
user.put("LocaleSidKey", "pt_BR");
user.put("TimeZoneSidKey", "America/Sao_Paulo");
PostMethod post = new PostMethod( instanceURL + "/services/data/v29.0/sobjects/User");
post.setRequestHeader("Authorization", "OAuth " + accessToken);
post.setRequestEntity(new StringRequestEntity(user.toString(), "application/json", null));
httpClient.executeMethod(post);
}catch(Exception e){}

I used the Chrome Postman plugin to POST the following:
Note that the resource URL was set to: (I'm assuming you are on AP1 based the the pod identifier in the ProfileId).
https://na5.salesforce.com/services/data/v29.0/sobjects/User
I got the response:
[
{
"message": "Duplicate Username.<br>The username already exists in this or another Salesforce organization. Usernames must be unique across all Salesforce organizations. To resolve, use a different username (it doesn't need to match the user's email address). ",
"errorCode": "DUPLICATE_USERNAME",
"fields": [
"Username"
]
}
]
Which is fine for testing purposes. It would have probably worked with a different username and free licenses.
Please confirm that your PostMethod URL is set to:
https://ap1.salesforce.com/services/data/v29.0/sobjects/User
In particular, check that there is no trailing slash on instanceURL.

Related

How Do I Authenticate a Service Account via Apps Script in order to Call a GCP Cloud Function

I'm trying hell hard to run a GCP Cloud Function from Apps Script. The Cloud function requires Authentication. However I keep getting thrown a "401 Error"
On My Google Project;
I've Created a Cloud function that requires Authentication
I've Created a Service Account that has Invoke (and edit) access to that function
I've Downloaded the JSON key for that service account and saved it as an object named CREDS in my Apps Script
This is my script so far:
const CREDS = {....JSON Key I downloaded from Cloud Console}
function base64Encode(str){
let encoded = Utilities.base64EncodeWebSafe(str)
return encoded.replace(/=+$/,'')
}
function encodeJWT(){
const privateKey = `Copied the PK from the CREDs file and replaced all the escaped whitespace as a string literal`;
let header = JSON.stringify({
alg: "RS256",
typ: "JWT",
});
let encodedHeader = base64Encode(header);
const now = Math.floor(Date.now() / 1000);
let payload = JSON.stringify({
"iss": "https://accounts.google.com",
"azp": "OAUTH CLIENT ID I CREATED ON GCP",
"aud": "OAUTH CLIENT ID I CREATED ON GCP",
"sub": CREDS.client_id,
"email": CREDS.client_email,
"email_verified": true,
// "at_hash": "TMTv8_OtKA539BBRxLoTBw", //Saw this in a reverse engineered Token but didnt know what to put
"iat": now.toString(),
"exp": (now + 3600).toString()
})
let encodedPayload = base64Encode(payload);
let toSign = [encodedHeader, encodedPayload].join('.')
let signature = Utilities.computeRsaSha256Signature(toSign, privateKey)
let encodedSignature = base64Encode(signature);
let jwt = [toSign, encodedSignature].join('.')
return jwt;
}
function testFireStore() {
let funcUrl = "https://[MY PROJECT].cloudfunctions.net/MyFunc"
const token = encodeJWT()
let options = {
headers:{
"Authorization": "Bearer " + token
}
}
let func = UrlFetchApp.fetch(funcUrl,options)
Logger.log(func.getContentText())
}
The actual Cloud func just gives a "Hello World" for now and it tests fine in the console
FYI, some steps I've done already
I've generated a token using gcloud on my local machine and used it in my apps script, that works fine
I've taken the said token and reverse engineered it on https://jwt.io
I've used the code here to create my JWT function which I checked back with https://jwt.io to ensure its in the correct format.
This Solution posted by #TheMaster in the comments to my solution solved the issue.
On the GCP side, I went in and enabled compute Engine and App Engine, then used this solution and it worked.
The only odd thing is that the target_audience requested there, I had to do a bit of reverse engineering to get it. I had to get the Identity Token from the command line tool, then use jwt.io to decode it, getting the AUD key...
but that aside, everythign worked like a charm

AcquireTokenAsync gets Response status code does not indicate success: 401 (Unauthorized)

As part of ECR, I'm changing our code to get token by using subject name instead of the thumbprint of our aad 1st party application.
I made a few changes:
1. I added the subject name to the "subject name + issuer" in the aad portal.
2. added the "sendX5c: true" parameter to the AcquireTokenAsync function call.
I'm getting the certificate from my machine but when trying to acquire the token I'm getting this exception: AADSTS7000213: Invalid certificate chain. with the inner exception of "Response status code does not indicate success: 401 (Unauthorized)".
Any idea what am I doing wrong or what is missing?
I followed this link: https://aadwiki.windows-int.net/index.php?title=Subject_Name_and_Issuer_Authentication
If you want to request Azure AD access token with client certificate, please refer to the following steps
Upload the information of the cert to Azure
a. get the information
$Thumbprint="930CFA423637129DB45921320B0BB451BD58A813"
$cert=Get-ChildItem -Path Cert:\LocalMachine\My\$Thumbprint
$rawCert = $cert.GetRawCertData()
$base64Cert = [System.Convert]::ToBase64String($rawCert)
$rawCertHash = $cert.GetCertHash()
$base64CertHash = [System.Convert]::ToBase64String($rawCertHash)
$KeyId = [System.Guid]::NewGuid().ToString()
b. upload the above information to Azure
Connect-AzureAD
$clientAadApplication=Get-AzureADApplication -Filter "AppId eq '<you client id>'"
New-AzureADApplicationKeyCredential -ObjectId $clientAadApplication.ObjectId -CustomKeyIdentifier "$base64CertHash" -Type AsymmetricX509Cert -Usage Verify -Value $base64Cert
Get access token(I use the sdk Microsoft.Identity.Client)
String subjectname="";
X509Store store = new X509Store(StoreName.My,StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectName, subjectname, false);
X509Certificate2 cer = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
var app = ConfidentialClientApplicationBuilder.Create("<client id>")
.WithAuthority(AzureCloudInstance.AzurePublic, "<tenant id>")
.WithCertificate(cer)
.Build();
var result = await app.AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" }).ExecuteAsync();
Console.WriteLine(result.AccessToken);
Console.Read();
For more details, please refer to the document and the sample

Unity/Android ServerAuthCode has no idToken on Backend

I have an unity app and use the google-play-games plugin with google *.aar versions 9.4.0. I lately changed my Backend (Google App Engine) from php to java. My problem is the following: in php the serverauthcode is used to get the users data (in JWT format) - it was working fine. So I changed to a Java servlet and I am failing since 2 days to get a valid idtoken. I am able to recieve the server auth code from my app and a valid token response is made by GoogleAuthorizationCodeTokenRequest (see code snippet). Unfortunately it does not contain any idtoken content but a valid auth_token. So I can not get the user id to identifiy the user. When I call tokenResponse.parseIdToken(); it is failing with a NullPointerException.
servlet code (authCode is the serverAuthCode I send from the play-games-plugin inside Unity to my GAE):
// (Receive authCode via HTTPS POST)
// Set path to the Web application client_secret_*.json file you downloaded from the
// Google Developers Console: https://console.developers.google.com/apis/credentials?project=_
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/mypath/client_secret.json";
// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(
JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
clientSecrets.getDetails().getTokenUri(),
clientSecrets.getDetails().getClientId(),
clientSecrets.getDetails().getClientSecret(),
authCode,
REDIRECT_URI) // Specify the same redirect URI that you use with your web
// app. If you don't have a web version of your app, you can
// specify an empty string.
.execute();
String accessToken = tokenResponse.getAccessToken();
// Get profile info from ID token -> HERE IT THROWS AN EXCEPTION.
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject(); // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
the token response looks like (its invalid now):
{
"access_token" : "ya29.CjA8A7O96w-vX4OCSPm-GMEPGVIEuRTeOxKy_75z6fbYVSXsdi9Ot3NmxlE-j_t-BI",
"expires_in" : 3596,
"token_type" : "Bearer"
}
In my PHP GAE I always had a idToken inside this constuct which contained my encrypted data. But it is missing now?! So I asssume I do somthing differently in Java or I made a mistake creating the new OAuth 2.0 Client on the google console.
I checked the accessToken manually via:
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.CjA8A7O96w-vX4OCSPm-GMEPGVIEu-RTeOxKy_75z6fbYVSXsdi9Ot3NmxlE-j_t-BI
{
"issued_to": "48168146---------.apps.googleusercontent.com",
"audience": "48168146---------.apps.googleusercontent.com",
"scope": "https://www.googleapis.com/auth/games_lite",
"expires_in": 879,
"access_type": "offline"
}
Is there something I do not see? Help is very much appreciated...
I found a root cause discussion inside the unity plugin "play-games-services" on github:
https://github.com/playgameservices/play-games-plugin-for-unity/issues/1293
and
https://github.com/playgameservices/play-games-plugin-for-unity/issues/1309
It seems that google switching their authentication flow. In the given links they are talking about adding the email scope inside the plugin to get the idtoken again. I'll try that in the next days and share my experience.
Here is a good explaination about what happens:
http://android-developers.blogspot.de/2016/01/play-games-permissions-are-changing-in.html
If you do what paulsalameh said here (Link to Github) it will work again:
paulsalameh: Sure. After you import the unitypackage, download NativeClient.cs and
PlayGamesClientConfig.cs from my commits (#1295 & #1296), and replace
them in the correct locations.
Afte that "unity play-services-plugin" code update you will be able to add AddOauthScope("email") to PlayGamesClientConfiguration, which allows your server to get the idtoken with the serverAuthCode again...
Code snippet from Unity:
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.AddOauthScope("email")
.AddOauthScope("profile")
.Build();
Now I am back in business:
{
"access_token" : "ya29.Ci8..7kBR-eBdPw1-P7Pe8QUC7e_Zv7qxCHA",
"expires_in" : 3600,
"id_token" : "eyJhbGciOi......I1NiE0v6kqw",
"refresh_token" : "1/HlSZOo......dQV1y4E",
"token_type" : "Bearer"
}

Gmail (for business) API doesn't allow to send email from Alias?

I want to email my customers using different "roles" (e.g. info# , customer-support#, tech-support#, no-reply#).
I've tried 2 approaches:
Multiple "users"/accounts in my Gmail for business application.
Single gmail
account with multiple aliases.
I started by setting up a Service Account with global delegation for my Gmail for Business application.
To test that it works, I've set up 2 users: lev#mydomain.com and root#mydomain.com. Indeed, I can successfully send email both from lev# and root#.
However, when I tried adding 5 distinct user accounts for my application, Gmail got paranoid of bots/abuse and asked me to prove that all the accounts are "human" including setting up passwords, signing in and SMS-text validation via phone. Moreover, they require different phones for different accounts to prove it's a different person. So the setup of the accounts becomes a major issue.
I also want to avoid creating multiple accounts since I'm paying for each one, and since semantically, all the roles are just a single account. So aliases seem like a better idea.
The problem is that when I'm trying to send email and set the "from" field to the alias (e.g. from:no-reply#mydomain.com), I'm getting the following exception:
Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Delegation denied for root#mydomain.com",
"reason" : "forbidden"
} ],
"message" : "Delegation denied for root#mydomain.com"
}
Anyone faced and solved this issue?
The authentication/credential code is as follows:
/*
* Set up a hashmap HashMap<String, Gmail> gmailServiceByAccount where
* gmailServiceByAccount.get(emailAccount) contains an authorized Gmail service
*/
private void prepareService(String emailAccount) throws Exception {
if (gmailServiceByAccount.containsKey(emailAccount)) {
return;
}
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(Config.getInstance().getProperty(Config.gmail_service_account))
.setServiceAccountPrivateKeyFromP12File(new File(Config.getInstance().getPathToGmailCredential()))
.setServiceAccountScopes(Arrays.asList(GmailScopes.GMAIL_COMPOSE))
.setServiceAccountUser(emailAccount)
.build();
gmailServiceByAccount.put(
emailAccount,
new Gmail.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(Config.getInstance().getProperty(Config.google_client_api_application_name))
.build());
}
And the code which sends the email is as follows:
/**
* Send an email using the parameters provided.
*
* #param fromPersonalName : the free text description of the "from" address (e.g. "Customer Suppport" or "No Reply").
* #param fromAddress : the email address of the sender, the mailbox account (e.g. customer-support#mydomain.com).
* #param to : the email address of the recepient.
* #param subject : Subject of the email.
* #param htmlContent : (may be null) The HTML-styled body text of the email.
* #param plainTextContent : (may be null) The plain text body of the email (e.g if the customer email client does not support or disables html email).
*/
public void sendMail(String fromPersonalName, String fromAddress, String to, String subject, String htmlContent, String plainTextContent)
throws Exception {
prepareService(fromAddress);
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage email = new MimeMessage(session);
InternetAddress tAddress = new InternetAddress(to);
InternetAddress fAddress = new InternetAddress(fromAddress);
fAddress.setPersonal(fromPersonalName);
email.setFrom(fAddress);
email.addRecipient(javax.mail.Message.RecipientType.TO, tAddress);
email.setSubject(subject);
Multipart multiPart = new MimeMultipart("alternative");
if (!StringValidation.isEmpty(plainTextContent)) {
MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(plainTextContent, "text/plain");
textPart.setHeader("Content-Type", "text/plain; charset=\"UTF-8\"");
multiPart.addBodyPart(textPart);
}
if (!StringValidation.isEmpty(htmlContent)) {
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlContent, "text/html; charset=\"UTF-8\"");
multiPart.addBodyPart(htmlPart);
}
email.setContent(multiPart);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
email.writeTo(bytes);
String encodedEmail = Base64.encodeBase64URLSafeString(bytes.toByteArray());
Message message = new Message();
message.setRaw(encodedEmail);
gmailServiceByAccount.get(fromAddress).users().messages().send(fromAddress, message).execute();
}
After additional research, it looks like the only option is to have multiple users.
The code I've posted indeed works for multiple users, but not for anything else.
I've tried multiple options including aliases and group email accounts. I'd either get "delegation denied" or "invalid grant" errors.
I've tried contacting Google For Business customer and tech support, but they don't support the API.
There's a great workaround to creating several users without having to go through phone validation. Just specify these users as "existing users" when you're signing into Google For Business initially, and activate them before you even transfer the domain.
For the account I've created without pre-existing users, I had to ask my friend's phones for phone validation.
You can now send emails using aliases as long as those aliases are defined for the user whose login credentials you're using.
This works for the Gmail for business only.
Setting up aliases to non-existent address can be tricky, so have a look at this how to set up a catch-all routing:
catchall-for-domain-aliases-in-gsuite-gmail
Just additionally to Ladi's post, it seem to be easier to setup now. Make an alias account and configure it so you can send emails (https://support.google.com/domains/answer/9437157?hl=en&ref_topic=6293345) and set the 'from' field on the message to the alias (but still use 'me' on the API call)

How do I use the Google API Explorer to test my own App Engine Endpoints using OAuth?

I have an Endpoints API deployed on App Engine. I have no problem using the Google API Explorer to make requests to API methods that do NOT require being logged in. The URL I'm using for that is:
https://developers.google.com/apis-explorer/?base=https://[MY_APP_ID].appspot.com/_ah/api
Where I am stuck is calling API methods that require the user to be logged in, such as this one:
#ApiMethod(name = "config.get",
clientIds = {"[MY_CLIENT_ID].apps.googleusercontent.com", "com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID"},
audiences = {"[MY_APP_ID].appspot.com"},
scopes = {"https://www.googleapis.com/auth/userinfo.email"})
public Config getConfig(User user) throws OAuthRequestException {
log.fine("user: " + user);
if (user == null) {
throw new OAuthRequestException("You must be logged in in order to get config.");
}
if (!userService.isUserAdmin()) {
throw new OAuthRequestException("You must be an App Engine admin in order to get config.");
}
...
On the API Explorer there's a switch top right that, when clicked, allows me to specify scopes and authorise. I'm doing that with just the userinfo.email scope checked. It makes no difference. The response I get from my call is:
503 Service Unavailable
- Show headers -
{
"error": {
"errors": [
{
"domain": "global",
"reason": "backendError",
"message": "java.lang.IllegalStateException: The current user is not logged in."
}
],
"code": 503,
"message": "java.lang.IllegalStateException: The current user is not logged in."
}
}
Back when Endpoints was in Trusted Tester phase, I remember there being a manual step in the OAuth2 Playground to get an ID token instead of an access token or some such thing. If that is still required, any mention of that seems to have disappeared from the Endpoints docs now and I see now way to swap out tokens in the API Explorer either.
I see you've got "com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID" in quotes. If that's not a typo in your transcription to Stack Overflow, that's a problem. The value is already a string, so you're just passing in the text com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID (not the actual client ID) as the whitelisted scope. That won't work. Try this instead:
#ApiMethod(name = "config.get",
clientIds = {"[MY_CLIENT_ID].apps.googleusercontent.com", com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {"[MY_APP_ID].appspot.com"},
scopes = {"https://www.googleapis.com/auth/userinfo.email"})
Edit: isUserAdmin is unsupported within Endpoints, and is likely a secondary cause of error. I'd suggest filing a feature request for supporting this method on the provided User object (we likely won't provide support for the user service itself, so it's separate from OAuth login.)
I don't know when this was introduced, but if you use OAuth2, instead of UserService.isUserAdmin() you can use OAuthServiceFactory.getOAuthService().isUserAdmin(EMAIL_SCOPE) where EMAIL_SCOPE is "https://www.googleapis.com/auth/userinfo.email".
This makes it easy to use the old OpenId or OAUth2:
boolean isAdmin = false;
try {
isAdmin = userService.isUserAdmin());
} catch (IllegalStateException e1) {
try {
isAdmin = OAuthServiceFactory.getOAuthService().isUserAdmin(EMAIL_SCOPE);
} catch (Exception e2) {}
}
The original question was asked several years ago, but maybe this will help others.

Resources