Unattended authorization into API - no longer possible? - docusignapi

A brief investigation shows that there once existed an authorization mode known as SOBO (for example, see docusign send on behalf functionality), which I find useful in a scenario when an application associates signing requests not with a logged-on user but with some other user. However I am unable to find any mention of it in current documentation; on the contrary, documentation clearly says only 3 grant types are supported, and all three involve impersonated user's actively expressing his consent. No way to just send user credentials, or, alternatively, have user express his consent just once on the docusign admin page, then reuse that consent for, say, one year or forever. Or some other way to avoid end user interaction.
Also, a requirement of using redirect URI to send back continuation data implies one has to either use on-premise solution or make one's application visible on the internet. No way to use public docusign service with on-premise application, right?

DocuSign is discouraging Legacy Header authentication because it requires your integration to hold on to the user's plain-text password. It also cannot support accounts that leverage Single-Sign On or Two-Factor Authentication. The long-term plan is likely to eventually retire it entirely, but there's no timeline for that. Documentation has been retired, but integrations that have used it in the past can still do so.
JWT auth is the equivalent replacement. With individual consent, a user grants the integration access once. Unless the user revokes that consent, the integration will be able to generate access tokens to act as the user indefinitely - JWT consent does not expire.
If you have an Organization with a Claimed Domain, an org admin can grant blanket consent to allow an integration to act as any user under that domain. If you'd like to grant consent to an integrator key owned by your organization, it's as simple as navigating to Org Admin > Applications > Authorize Application. Granting consent to a 3rd party app is similar to the Individual Consent workflow, but has extra scopes as documented here: https://developers.docusign.com/esign-rest-api/guides/authentication/obtaining-consent
Note that while JWT auth does require a redirect URI to be registered, an integration doesn't necessarily need to 'catch' the user after they've granted consent. While it would be recommended that the landing page trigger the user to move forward in the workflow, it's acceptable to point your redirect URI to https://www.example.com, grant consent, and then generate an access token.

I know this question has already been answered, but I'll post this answer here just in case someone still needs to do this. This method does not require user's consent. The below is Node.js / JS but can be easily translated into whatever language with the basics below.
// set default authentication for DocuSign; pulls data from this account
var auth = {
'Username': '(user email)',
'Password': '(user password)',
'IntegratorKey': '(api key found in admin)',
};
var options = {
'uri': 'https://www.docusign.net/restapi/v2/login_information',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above
The above returns a response:
{
"loginAccounts": [
{
"name":"Your Company Name",
"accountId":"0000000",
"baseUrl":"https://{your_subdomain}.docusign.net/restapi/v2/accounts/0000000",
"isDefault":"true",
"userName":"User's Name",
"userId":"(36 character UUID)",
"email":"user#example.com",
"siteDescription":""
}
]
}
At this point, you can have to save the baseUrl and accountId that is returned. For the baseUrl, you only need to save the the sub-domain and domain url (https://{your_subdomain}.docusign.net), not the url paramters after that.
Now you can have enough information to make requests. The below example request pulls all the templates under this account.
var options = {
'uri': baseUri+'/accounts/'+accountId+'/templates',
'method': 'GET',
'body': '',
'headers': {
'Content-Type': 'application/json',
// turns the auth object into JSON
'X-DocuSign-Authentication': JSON.stringify(auth)
}
};
// send off your request using the options above

Related

passing properties in callback function nodejs

I'm using paypal-rest-sdk. Problem I'm facing is, when I'm making an authorizationUrl call, I want to pass some parameters which can be accessed in the redirected URL.
Below is my code
import paypal from 'paypal-rest-sdk';
const openIdConnect = paypal.openIdConnect;
paypal.configure({
mode: "sandbox"
client_id: //MyClientId,
client_secret: //MySecretId,
openid_redirect_uri: `http://myRedirectionEndpoint/account/domestic/paypal/callback?state={accountId:5e8c2291d69ed1407ec86221}`
});
openIdConnect.authorizeUrl({scope: 'openid profile'});
Adding query parameter state gives the error as invalid redirectUri
What is the best way to pass the data that needs to be used after redirection
I think you are slightly misunderstanding how oauth authorization works. Basically if you want to get any data you need to do this AFTER you consume the callback and validate the user in your system as well.
Have you ever seen for Google/github etc openid auth provider returning some data that corresponds to the caller system's data? It's not possible.
You are probably confusing this with webhook where the caller system calls a webhook with some data internally and you capture it. Which is commonly used in payment transactions.
But the auth is slightly different. For auth there are 3 systems.
the actual auth provider (Paypal/google/github) etc.
an Identity provider which basically gets profile data etc and other than for enterprise systems these two systems are simply same.
the caller system which is your NodeJS service in this case.
=> Now caller-system calls the auth provider to get some kind of code generally an auth code. This means the user exists in auth system let's say Google.
=> Then the caller-system calls the identity provider with that auth code checking if the user is there in identity provider(idp) as well and the idp returns access_token, id_token, refresh_token etc (as I said most of the time these are same systems). But consider amazon, let's say you want to login to Amazon with your Google account. You have a Google account alright but you don't have amazon account. So you will get the auth code but will not get the id_token.
=> Now the id_token most of the time contains some basic info of the user in JWT format. But Now the ACCESS_TOKEN is used to do all the other calls to your system(caller system). Now as I said id_token some kind of user data. You can have a db table mapping userid with account number in your NodeJs service.
=> Make an endpoint to get the account number or something which takes access_token and id_token. First validate the access_token and verify the signature of the id_token then decrypt the token to get basic user info. and use that id to fetch the data from your table and use that data.
After Edit:
You can see in the doc:
paypal.configure({
'openid_client_id': 'CLIENT_ID',
'openid_client_secret': 'CLIENT_SECRET',
'openid_redirect_uri': 'http://example.com' });
// Authorize url
paypal.openIdConnect.authorizeUrl({'scope': 'openid profile'});
// Get tokeninfo with Authorize code
paypal.openIdConnect.tokeninfo.create("Replace with authorize code", function(error, tokeninfo){
console.log(tokeninfo);
});
// Get userinfo with Access code
paypal.openIdConnect.userinfo.get("Replace with access_code", function(error, userinfo){
console.log(userinfo);
});
When you get the auth code, you use it to call the paypal.openIdConnect.tokeninfo.create and get the tokens. Then use those tokens to call the paypal.openIdConnect.userinfo.get to get the user Info. Now when you get the userinfo you will be able to create the db row that you wanted to create.
You can add those two below calls in your /callback route.

Python Salesforce API Authentication

I am trying to automate creating tickets in Salesforce. For this, I am using API with Python. I have got the Client ID and Client secret for my registered python Application. I have read many questions and as per the security purpose, I do not want to use the "user-password" flow for my script. Is there any way that I can only use "CLIENT ID" and "CLIENT SECRET" to get the access token where I can pass this access token in bearer header for other calls
import requests
params = {
"grant_type": "client_credentials",
"client_id": client_id, # Consumer Key
"client_secret": client_secret, # Consumer Secret
}
r=requests.post("https://login.salesforce.com/services/oauth2/token", params=params)
access_token = r.json().get("access_token")
instance_url = r.json().get("instance_url")
print("Access Token:", access_token)
You'll always need a SF user account. There's no way to just make a backend system talk to SF system. Salesforce treats everybody as user so you need to waste an account for "integration user" - but in return you can control access to tables, columns, functionalities just like you control real humans' access. This goes all the way down to the underlying Oracle database and database user privileges.
Whether you use OAuth2 flows (including client secrets) or maybe some certificate-based authentication - there will be always some element of "username and password" required. Best you can do is to make sure your app doesn't need to see & store the password, instead showing normal SF login prompt and on successful login user is redirected to your app to continue with known session id...
There might be something you'll be able to automate more if your app and SF use same Single Sign-On but broadly speaking... You have to either let users login to SF via your app or create the tickets as some dedicated admin user (and then you store this user's credentials in your app)

Asking for user info anonymously Microsoft Graph

In an old application some people in my company were able to get info from Microsoft Graph without signing users in. I've tried to replicate this but I get unauthorized when trying to fetch users. I think the graph might have changed, or I'm doing something wrong in Azure when I register my app.
So in the Azure portal i have registered an application (web app), and granted it permissions to Azure ad and Microsoft graph to read all users full profiles.
Then I do a request
var client = new RestClient(string.Format("https://login.microsoftonline.com/{0}/oauth2/token", _tenant));
var request = new RestRequest();
request.Method = Method.POST;
request.AddParameter("tenant", _tenant);
request.AddParameter("client_id", _clientId);
request.AddParameter("client_secret", _secret);
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("resource", "https://graph.microsoft.com");
request.AddParameter("scope", "Directory.Read.All");
I added the last row (scope) while testing. I still got a token without this but the result is same with or without it.
After I get a token I save it and do this request:
var testClient = new RestClient(string.Format("https://graph.microsoft.com/v1.0/users/{0}", "test#test.onmicrosoft.com")); //I use a real user here in my code ofc.
testRequest = new RestRequest();
testRequest.Method = Method.GET;
testRequest.AddParameter("Authorization", _token.Token);
var testResponse = testClient.Execute(testRequest);
However now I get an error saying unauthorized, Bearer access token is empty.
The errors point me to signing users in and doing the request, however I do not want to sign a user in. As far as i know this was possible before. Have Microsoft changed it to not allow anonymous requests?
If so, is it possible to not redirecting the user to a consent-page? The users are already signed in via Owin. However users may have different access and i want this app to be able to access everything from the azure ad, regardless of wich user is logged in. How is the correct way of doing this nowadays?
Or am I just missing something obvious? The app has been given access to azure and microsoft graph and an admin has granted permissions for the app.
Edit: just to clarify, i tried both "Authorization", "bearer " + _token.Token, and just _token.Token as in the snippet.
Yes, it's still possible to make requests to Graph without a user present using application permissions. You will need to have the tenant admin consent and approve your application.
Edit / answer: Adding the 'Authorization' as a header instead of a parameter did the trick. It works both with 'bearer token' and just 'token'

chrome.identity.LaunchWebAuthFlow: How to implement logout in a web app using oauth2

I am working on some client side web app like a chrome extension that needs access to outlook mail and calendar. I followed the instruction on https://dev.outlook.com/RestGettingStarted and successfully got access and refresh tokens to retrieve data.
However, I cannot find any way of implementing "logout". The basic idea is to let user sign out and login with a different outlook account. In order to do that, I removed cached tokens, requested access tokens in interactive mode. The login window did pop out, but it took any valid email address, didn't let me input password and finally returned tokens for previous account. So I was not able to really use a different account until the old token expired.
Can anyone please tell me if it is possible to send a request to revoke the tokens so people can use a different account? Thanks!
=========================================================
Update:
Actually it is the fault of chrome.identity api. I used chrome.identity.LaunchWebAuthFlow to init the auth flow. It caches user's identity but no way to remove it. So we cannot really "logout" if using this api.
I used two logouts via launchWebAuthFlow - first I called the logout link to my app, then secondly, I called the logout link to Google.
var options = {
'interactive': false,
'url': 'https://localhost:44344/Account/Logout'
}
chrome.identity.launchWebAuthFlow(options, function(redirectUri) {});
options = {
'interactive': false,
'url': 'https://accounts.google.com/logout'
}
chrome.identity.launchWebAuthFlow(options, function(redirectUri) {});

Using Google API's for one's own account without OAuth

Specifically, I'd like to use the Gmail API to access my own mail only. Is there a way to do this without OAuth and just an API key and/or client id and secret?
Using an API key like:
require('googleapis').gmail('v1').users.messages.list({ auth: '<KEY>', userId: '<EMAIL>') });
yields the following error:
{ errors:
[ { domain: 'global',
reason: 'required',
message: 'Login Required',
locationType: 'header',
location: 'Authorization' } ],
code: 401,
message: 'Login Required' }
I suppose that message means they want a valid OAuth "Authorization" header. I would do that but I suppose that's not possible without presenting a webpage.
The strict answer to "Is there a way to do this without OAuth and just an API key and/or client id and secret?" is no.
However, you can achieve what you are looking for using OAuth. You simply need to store a Refresh Token, which you can then use any time to request an Auth Token to access your gmail.
In order to get the refresh token, you can either write a simple web app to do a one time auth, or follow the steps here How do I authorise an app (web or installed) without user intervention? (canonical ?) which allows you to do the whole auth flow using the Oauth Playground.
The question is rather old, but the problem is not. For now Google API has an option to create service accounts. I think it suits for everybody who wants "just connect application to its own google workspace" and not to do some actions on users behalf. Google documentation writes about it:
Typically, an application uses a service account when the application uses Google APIs to work with its own data rather than a user's data. For example, an application that uses Google Cloud Datastore for data persistence would use a service account to authenticate its calls to the Google Cloud Datastore API.
Here is the example in Java (there was no JS, but the meaning is clear):
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
.createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));
SQLAdmin sqladmin =
new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
SQLAdmin.Instances.List instances =
sqladmin.instances().list("exciting-example-123").execute();

Resources