Salesforce Callouts using Named Credentials - salesforce

In Salesforce, Making a POST callout using apex and generating the bearer token.
Using the bearer token, making a GET callout and then parsing the JSON response.
However when I am trying to replicate the same procedure using Named Credential I fail to achieve the requirement.(O AUth 2.0)
HTTPRequest feedRequest = new HTTPRequest();
feedRequest.setEndpoint('callout:Salesforce_NC/the Get Callout Enpoint');
feedRequest.setHeader('Content-Type', 'application/json');
feedRequest.setMethod('GET');
HTTP http = new HTTP();
HTTPResponse feedResponse = http.send(feedRequest);
System.debug(feedResponse.getBody() + feedResponse.getStatusCode());

Related

HttpResponse[Status=Found, StatusCode=302]" while integrating Salesforce to AzureDevops

Integrating Salesforce to Azuredevops,
Creating Issue/Task/Bug to Azuredevops from the Salesforce custom page by using POST method RestApi.
Doing the same from postman service by using same url ,accesstoken etc and i am able to create task in the Azuredevops.
But issue is when i try the same from salesforce I am getting the response back as
"System.HttpResponse[Status=Found, StatusCode=302]" and <html><head><title>Object moved</title></head><body> <h2>Object moved to <a href="https://*******.*****.visualstudio.com/_signin*********************
I am using Namedcredential in the salesforce,
and written code like below,
Httprequest request = new HttpRequest();
Http http = new Http();
request.setMethod('POST');
request.setEndpoint('callout:Namedcred' + '/*****/******/_apis/wit/workitems/$issue?api-version=6.0' );
request.setHeader('Content-Type', 'application/json-patch+json');
request.setHeader('Authorization', '{!$Credential.Password}');
request.setBody(requestBody);
HttpResponse response = http.send(request);
I tried the same from the postman service and everything working good and also getting 200 OK status

Azure AD Bearer Token has wrong "aud" claims

I am trying to use AAD delegated permission Bearer tokens for a Visio VSTO addin to create SharePoint Online pages using CSOM. Initially I was able to get this working entering username / password following Modern Authentication with CSOM for Net Standard
However, I would like for the user to select an existing AAD account. When I attempt to use the following code the Bearer token "aud" claim is consistently set to "00000003-0000-0000-c000-000000000000" which is the Graph API. Whilst a ClientContext object is returned I am getting a HTTP 401 Unauthorized error when performing a page lookup.
The code is as follows
//
// Get Client App
//
var ClientApp = (PublicClientApplication)PublicClientApplicationBuilder.Create(<AAD App ID>)
.WithDefaultRedirectUri()
.WithTenantId(<AAD Tenant ID>)
.WithAuthority(AzureCloudInstance.AzurePublic, <AAD Tenant ID>)
.Build();
//
// Prompt for user to select preferred AAD account
// The returned JWT Bearer Token "aud" claim is 00000003-0000-0000-c000-000000000000
//
var Token = ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync()
.GetAwaiter()
.GetResult();
//
// Get client Context
//
var ClientContext = AuthenticationManager.GetAzureADAccessTokenAuthenticatedContext(<SharePoint Site URL>, Token.AccessToken);
//
// Using the Client Context to query the Site results in HTTP 401
//
ClientContext.Load(ClientContext.Web, p => p.Title, t => t.Description);
ClientContext.ExecuteQuery();
Looking at the code for the AuthenticationManager class in the above link I can see that the AAD Bearer request is passing the following resource request parameter to the SharePoint online URL:
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
So it seems that AAD is setting the Bearer token "aud" claim based upon this parameter. However, when I try and add this parameter using 'WithExtraQueryParameters' I am getting the following error: "AADSTS901002: The 'resource' request parameter is not supported"
Ok, I figured out the problem. The scope needs to be prefixed with the resource:
string[] scopes = { "https://<domain>.sharepoint.com/AllSites.Write", "user.read" }
Then retrieve the token
this.Token = await ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync();

How to get client_assertion string

I've written EWS application in C++. Currently it supports Basic and NTLM authentication, now trying to support OAuth authentication
Since it is C++ application I can't use .NET AcquireToken, so I need to post the below request for OAuth authentication
POST https://login.microsoftonline.com/b9bd2162xxx/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
resource=https://tailspin.onmicrosoft.com/surveys.webapi
&client_id=87df91dc-63de-4765-8701-b59cc8bd9e11
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=eyJhbGci...
&grant_type=authorization_code
So my question is, if I'm constructing the request, how can I get client_assertion string? is there any API\open source library to get this string using .pfx\X.509 certificate?
Based on the value of grant_type, you were using the Authorization Code Grant Flow. This flow is used to a interactive app. If this flow is you want to use, there is no need to provider the client_assertion and client_assertion_type.
You can refer the request below about this flow.
1.Request an authorization code:
https://login.microsoftonline.com/{tenant}/oauth2/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&resource=https%3A%2F%2Fservice.contoso.com%2F
&state=12345
2.Use the authorization code to request an access token:
POST /{tenant}/oauth2/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=2d4d11a2-f814-46a7-890a-274a72a7309e
&code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrqqf_ZT_p5uEAEJJ_nZ3UmphWygRNy2C3jJ239gV_DBnZ2syeg95Ki-374WHUP-i3yIhv5i-7KU2CEoPXwURQp6IVYMw-DjAOzn7C3JCu5wpngXmbZKtJdWmiBzHpcO2aICJPu1KvJrDLDP20chJBXzVYJtkfjviLNNW7l7Y3ydcHDsBRKZc3GuMQanmcghXPyoDg41g8XbwPudVh7uCmUponBQpIhbuffFP_tbV8SNzsPoFz9CLpBCZagJVXeqWoYMPe2dSsPiLO9Alf_YIe5zpi-zY4C3aLw5g9at35eZTfNd0gBRpR5ojkMIcZZ6IgAA
&redirect_uri=https%3A%2F%2Flocalhost%2Fmyapp%2F
&resource=https%3A%2F%2Fservice.contoso.com%2F
&client_secret=p#ssw0rd
//NOTE: client_secret only required for web apps
More detail about this flow, please refer the documet below:
Authorize access to web applications using OAuth 2.0 and Azure Active Directory
Update
string clientId = "";
string thumbprint = "";
X509Certificate2 cert = GetCertificate(thumbprint);
string resource = "";
string authority = "https://login.microsoftonline.com/{tenant}";
AuthenticationContext authContext = new AuthenticationContext(authority);
var resoult= authContext.AcquireTokenAsync(resource, new ClientAssertionCertificate(clientId, cert)).Result;
Console.WriteLine(resoult.AccessToken);

How to pass Azure AD authentication tokens so AngularJS can use them to call webapi

After successful AD authentication from my MVC web app, I set the token in the header so that client side scripts can make use of it to access the web api.
OnAuthorizationCodeReceived() after a successful login
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, graphResourceId);
HttpContext.Current.Request.Headers.Add("Authorization", "Bearer " + result.AccessToken);
}
In the last line I set the "Authorization" header. However I can't seem to make use of that from my AngularJS end to make api calls! What am I missing here? Requirement is that I don't have to use ADAL to authenticate from the AngularJS since web app already authenticates the user and have passed me the valid tokens.
You could try to save the access token in cookie :
HttpCookie tokenCookies = new HttpCookie("token");
tokenCookies.Value = result.AccessToken;
tokenCookies.Expires = DateTime.Now.AddHours(1);
HttpContext.Current.Response.Cookies.Add(tokenCookies);
Then making ajax call with access token from cookie on client side .

Call Parse.com REST services from Google App Engine

I have created a database in the Parse.com cloud. I now need to write a Servlet in my Google App Engine application to call the REST services on Parse. The REST services require
user authentication which is the Parse app id and the Javascript key.
...
URL url = new URL("https://api.parse.com/1/classes/OBJECT");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Content-Type", "application/json");
Base64 enc = new Base64();
String userpassword = "{PARSE_APP_ID}" + ":" + "javascript-key={PARSE_JS_KEY}";
String encodedAuthorization = enc.encodeBase64String(userpassword.getBytes());
connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
connection.setRequestMethod("GET");
...
I use org.apache.commons.codec.binary.Base64 for encoding to get REST call authenticated.
The Parse.com REST API recommends using the following request format to make a HTTP call:
https://myAppID:javascript-key=myJavaScriptKey#api.parse.com/1/classes/GameScore/Ed1nuqPvcm
The problem is that I kept getting
{"error":"unauthorized"}
Is there anyone having the experience with working with calling a authenticated REST service? Thanks!
EDIT.
URL url = new URL("https://api.parse.com/1/classes/OBJECT");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("X-Parse-Application-Id", "{APP_ID}");
connection.setRequestProperty("X-Parse-REST-API-Key", "REST_ID");
connection.setRequestProperty("Content-Type", "application/json");
I still got the same error while the responseCode was "200".
Cheers,
Use the header version of authentication instead:
connection.setRequestProperty("X-Parse-Application-Id", "app id here");
connection.setRequestProperty("X-Parse-REST-API-Key", "rest key here");

Resources