I want to implement DocuSign Service integration authentication with jwt flow.
I’ve generated valid jwt (validated on jwt.io) and I can successfully obtain access token based on jwt according to https://docs.docusign.com/esign/guide/authentication/oa2_jwt.html#requesting-the-access-token
I found on this blog post: https://www.docusign.com/blog/dsdev-docusign-developers-look-inside-new-authentication-apis/ that sub claim should be omitted in case application represents user in the system (which I need):
sub: The user id of the principal you are requesting a token for. If omitted a token will be issued to represent the application itself instead of a user in the system. Required: No
But in next step "Obtaining the Base URI" that states:
The first thing you should do after getting your access token is to use the /oauth/userinfo endpoint to get user’s account and base URI information that you’ll use to access the DocuSign API.
GET /oauth/userinfo Authorization: Bearer eyJ0eX...AnHDQ0bbA
Fails with status code 401 Unauthorized with response body details:
{
"error": "internal_server_error",
"reference_id": "e051ca48-....f0f"
}
I also tried to call Login (from AuthenticationApi - DocuSign.NetCore 1.1.0 nuget package), with default authorization header containing an access token like this:
Configuration.Default.DefaultHeader.Add("Authorization", string.Format("Bearer {0}", accessToken));
AuthenticationApi authApi = new AuthenticationApi(Configuration.Default);
LoginInformation loginInfo = authApi.Login();
Code above works only if I use OAuth2 access token that I can obtain directly from api explorer:
https://apiexplorer.docusign.com/#/esign/restapi?categories=Authentication&tags=Authentication&operations=login&mode=basic
but when I use access token that I've obtained by following official documentation (described above) I get exception:
DocuSign.eSign.Client.ApiException: ‘Error calling Login: {
“errorCode”: “USER_AUTHENTICATION_FAILED”,
“message”: “One or both of Username and Password are invalid. Invalid access token”.
What seems to be that I'm missing?
As Amit says, you need to provide a userID (guid format). The fact that you're receiving the Consent Required is good news: you're almost there.
Your user can grant consent individually or you can grant blanket consent at the org level if you have org admin turned on.
See my video or blog post for how to individually grant consent.
Ommitting sub is not yet implemented, it is a future state which is yet to be implemented. As of now, you always need to pass sub in the call, and you can get Accesstoken for a user only. That's a blog link with the big picture overview of what's coming with new OAUTH, but whats currently implemented is available at DS Docs
Related
I am using LinkedIn oauth 2.0 api implementation and all went well to setup and retrieval of Access Token, but then i revoked the AccessToken after login in LinkedIn and also deleted the AccessToken i saved in my DB.
The idea is to give an option to Re-Authorize the account as given in point 1 below.
Once the request is made, one of the following occurs:
If the member has not previously accepted the application's permission request, or the grant has expired or been manually revoked by the member, the browser is redirected to LinkedIn's authorization screen as shown in the screenshot below. When the member completes the authorization process, the browser is redirected to the URL provided in the redirect_uri query parameter.
If there is a valid existing permission grant from the member, the authorization screen is bypassed and the member is immediately redirected to the URL provided in the redirect_uri query parameter.
What actually happen is that when ever i redirect user to the Authorization URL, it goes for option 2 written above rather than option 1, and with a null Authorization Code. Below given is my Code on click of a button.
var permissions = new string[] { "r_basicprofile", "r_emailaddress", "w_member_social" };
var authUrl = client.GetAuthorizationUrl(permissions,true);
return new RedirectResult(authUrl.ToString());
and below given is the method that get called when LinkedIn callback redirect_uri
public ActionResult LinkedInAuthentication(string code, string state)
{
//the code parameter is null here
}
This should not be the behavior, please help.
Also can i know how to request for RefreshToken?
Ahhh issue resolved, after a lot of search, i started reading complete linkedin documentation and found below given statement under integration > SignIn with LinkedIn rather under Authentication > Authorization Code Flow (3-legged OAuth) which actually explains each step of authentication. The link that explains this is
https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context?trk=eml_mktg_gco_dev_api_comms#authenticating-members
Authenticating Members
New members logging in to your service for the first time will need to follow the Authenticating with OAuth 2.0 Guide. When requesting an authorization code in Step 2 of the OAuth 2.0 Guide, make sure to request the r_liteprofile and/or r_emailaddress scopes!
so changing
var permissions = new string[] { "r_basicprofile", "r_emailaddress", "w_member_social" };
to
var permissions = new string[] { "r_liteprofile", "r_emailaddress"};
resolved the issue.
Hope someone else will be benefited to :)
Also if someone can help how to enable basicprofile and fullprofile features in app? That will be great.
Scenario
I have an Exchange Online environment and service/daemin (no interactive user) application on the Azure VM. Service uses EWS managed API to work with emails in the mailbox of any tenant user. Now EWS client uses Basic authentication that, according to Microsoft, will become unsupported in EWS to access Exchange Online.
Question/Issue
So, I need to find a way to get valid access token for service/daemon application to use with EWS managed API.
My findings
The following article shows an example of using OAuth 2.0 with EWS managed API. This example works, but it uses interactive method of getting consent (sign-in form appears allowing user authenticate themselves and grant requested permission to application) that is not suitable for service/daemon app scenario, because there is no interactive user.
For service/daemon application I need to use client credential authentication flow.
Registered application
Using admin account on https://aad.portal.azure.com portal I registered application with Azure Active Directory. Added client secret for registered application.
Aforementioned article uses https://outlook.office.com/EWS.AccessAsUser.All as a scope. But I did not find permission with such a URL on the portal. I found only the following permissions under Office 365 Exchange Online > Application permissions > Mail:
https://outlook.office365.com/Mail.Read Allows the app to read mail in all mailboxes without a signed-in user
https://outlook.office365.com/Mail.ReadWrite Allows the app to create, read, update, and delete mail in all mailboxes without a signed-in user.
I added both of them and granted admin consent for all users.
Getting access token
For testing purposes and simplicity I did not use any auth libraries (ADAL, MSAL etc.). I used Postman to get access token, then set token variable in debug (see code snippet later in the post).
I tried different endpoints to get acess token.
OAuth 2.0 token endpoint (v2)
POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token
grant_type=client_credentials
client_id=***
client_secret=***
scope=https://outlook.office.com/EWS.AccessAsUser.All
Sending this request produces the following error response:
AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope https://outlook.office.com/EWS.AccessAsUser.All is not valid.
I tried changing scope to https://outlook.office.com/.default. Access token was returned, but it appeared to be invalid for EWS. EWS client throws 401 error with the following value of x-ms-diagnostics response header:
2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
OAuth 2.0 token endpoint (v1)
POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/token
grant_type=client_credentials
client_id=***
client_secret=***
resource=https://outlook.office.com
Access token was returned, but also appeared to be invalid for EWS. EWS client throws 401 error with the same value of x-ms-diagnostics response header as described ealier in #1.
Use aquired access token with EWS managed API
Here is code sample that I used to test EWS client with access token acquired in Postman:
var token = "...";
var client = new ExchangeService
{
Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
Credentials = new OAuthCredentials(token),
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,
"user#domain.onmicrosoft.com"),
};
var folder = Folder.Bind(client, WellKnownFolderName.SentItems);
We had a similar problem: We wanted to use a Service Account to connect to a single mailbox and just doing some stuff with the EWS API (e.g. searching in the GAL) and the full_access_as_app seems like an overkill.
Fortunately it is possible:
Follow the normal "delegate" steps
And use this to get a token via username/password:
...
var cred = new NetworkCredential("UserName", "Password");
var authResult = await pca.AcquireTokenByUsernamePassword(new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" }, cred.UserName, cred.SecurePassword).ExecuteAsync();
...
To make this work you need to enable the "Treat application as public client" under "Authentication" > "Advanced settings" because this uses the "Resource owner password credential flow". (This SO answer helped me alot!)
With that setup we could use a "tradional" username/password way, but using OAuth and the EWS API.
You can protect your client application with either a certificate or a secret. The two permissions that I needed to get this to work were Calendars.ReadWrite.All and full_access_as_app. I never tried acquiring my token via PostMan, but use AcquireTokenAsync in Microsoft.IdentityModel.Clients.ActiveDirectory. In that call, the resource parameter I use is https://outlook.office365.com/. It's pretty simple once you know all the little twists and turns. And full disclosure: I was one lost puppy until MSFT support helped me through this. The doc on the web is often outdated, conflicting, or at best, confusing.
You need to register your app in Azure and use certificate based authentication. https://blogs.msdn.microsoft.com/emeamsgdev/2018/09/11/authenticating-against-exchange-web-services-using-certificate-based-oauth2-tokens/
I run into the same issue while following Microsoft official docs for OAuth 2.0 client credentials flow
According to the Microsoft identity platform and the OAuth 2.0 client credentials flow, the scope "should be the resource identifier (application ID URI) of the resource you want, affixed with the .default suffix" (see default scope doc).
So the question is how to convert https://outlook.office.com/EWS.AccessAsUser.All into the resource identifier.
Experimentally I manage to make it working using scope=https://outlook.office365.com/.default. I granted full_access_as_app (Office 365 Exchange Online / Application permissions) and got administrator consent for it.
I did face this issue while implementing OAuth for EWS. My application is not using EWS Managed API. Here is what all I did to make it working.
Added permission Office 365 Exchange Online > full_access_as_app to application.
Acquired access token for scope https://outlook.office365.com/.default.
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
form-data = {
client_id,
client_secret,
grant_type: 'client_credentials',
scope: 'https://outlook.office365.com/.default',
};
Added access token as Authorization header and ExchangeImpersonation SOAP header to the request.
<SOAP-ENV:Header>
<t:ExchangeImpersonation>
<t:ConnectingSID>
<t:PrimarySmtpAddress>user#domain.com</t:PrimarySmtpAddress>
</t:ConnectingSID>
</t:ExchangeImpersonation>
</SOAP-ENV:Header>
Late answer, but since this seems to come up, and I was just working with this... why not.
If you use Microsoft's v2.0 URLs for OAUTH2 (https://login.microsoftonline.com/common/oauth2/v2.0/authorize and .../common/oauth2/v2.0/token) then the scope for Office 365 EWS is:
https://outlook.office365.com/EWS.AccessAsUser.All
You'll probably want to combine this scope with "openid" (to get the signed in user's identity) and "offline_access" (to get a refresh token). But then offline_access may not be necessary when using client credentials (because you don't have to prompt a human user for them every time you need an access token).
In other words:
params.add("client_id", "...")
...
params.add("scope", "openid offline_access https://outlook.office365.com/EWS.AccessAsUser.All")
If using v1 OAUTH2 URLs (https://login.microsoftonline.com/common/oauth2/authorize and .../common/oauth2/token) then you can use a "resource" instead of a "scope". The resource for Office 365 is https://outlook.office365.com/.
Or in other words:
params.add("resource", "https://outlook.office365.com/")
Note that in the latter case, you're not asking for any scopes (it's not possible to combine "resource" with scopes). But the token will automatically cover offline_access and openid scopes.
I used this method successfully:
Install Microsoft Authentication Library module ( MSAL.PS)
https://www.powershellgallery.com/packages/MSAL.PS/4.2.1.3
Configure Delegate Access as per MSFT instructions: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
Configure ApplicationImpersonation for a service account as normal
Grab your token
$cred = Get-Credential
$clientid = ""
$tenantid = ""
$tok = Get-MsalToken -ClientId $clientid -TenantId $tenantid -UserCredential $cred -Scopes "https://outlook.office.com/EWS.AccessAsUser.All"
I want to use Microsoft Graph to fetch manager details from my C# based bot.
I am able to fetch my profile by using this query:
await new HttpClient()
.GetWithAuthAsync(token.AccessToken,
"https://graph.microsoft.com/v1.0/me/");
but when I try to fetch the manager, I am getting not able to get a response:
await new HttpClient()
.GetWithAuthAsync(token.AccessToken,
"https://graph.microsoft.com/v1.0/users/abc#domain.com/manager");
I am unable to understand what is wrong with the query
please help.
Please find below access token:
https://9c9ca0db.ngrok.io/api/OAuthCallback?code=AQABAAIAAADX8GCi6Js6SK82TsD2Pb7rgKFM6GqiboAOn6WZitAqMLG2xkiduiMIz1slVYvjSZeevZcHogj8vmYwZH1JfaqgX1CXsBs2l7bCn1lwhZh2bq6B4LlxeJWku8zZI5hiY2mLReHWWiuQtZp4J5JJ_RVvbe6eBfgsamlCYhRPKMAfsuRBri-mQ5nJCYmVkdYOY6aGxblY2mzZL85mwogRECROLc0PsQohR1Sw0rRTon7JvHl8Pc5-GxxFYwtClp66EWnhoy8FV5dFBSOfOS_wNcijwKkA-RXvaZ2yscOnfCOKRaEL2FAUm6MAz7StrJQD0y3a1_-g97IxdtQenMNwhkSNp6wiLQsD0DzFr3zfLuIr_07ttOy07NknTJ9OPjneWQcONKUhQvLAfy-JsW4VwgOznwEcIT8K7ML-QpGXfNB1-igjm0b5x0ucHz76FQfLHxWGW2x9tsyg14NcKfpHlIsEDmHEooIGm0RCjYMuuo6uXfMCDIAMVwzUx4ehKZRXF3oNi--I889Gjfm2DeClhDYkg_ErasBgT2LLB1sLo2bPC8_65EDRQRE7sawDeyVa4sasasZ-OaN-E41dwu6re7tJcfbphpTgS9uMkkhhyic6HIwzg1iRk8sqo0_vQ6uAMtB7LDmSny7vN_3kNWFamR9u-_vOMwSW2sRZkf8S0QxjmuDmVkrH32iKx1dsszmXmtjuUtZoLr400LjNHXEb3MWUjbLWxL3u5xassasyX1LrcXYGLF3bPiZigX_Q3-8bFAHjV3-jvHxgIFd7NLtkR4socHO7Dx99ejDCnQ_sCoyFQVhRUE8iAA&state=H4sIAAAAAAAEAG2Oyw3CMAxAred4QH7EAO_SHKKgsgLhwQR0gTQ0JuHGVpF2SpXDFlZv1cd7tDwAkY0B_7WArc4cPNVJMZ_QTN9XjH6WNcg5JspU47EdSkYW3HIVthNW1MqRfx9JCIslkNTaeYCfKxDiEc56Xh1PRFhVm7un5nVmGpQ0Xz-MgX2l2E_qgomUnK9fS7SvSLXWmhoRYK0JYzMdd2twBvnWWUE3LAAAA0&session_state=e4d12345-4013-4edb-8487-35ef1763f323
The "access token" you've provided is not an Access Token. That is an Authorization Code that you would use to obtain an Access Token. More specifically, that is the redirection URI with query params that include the code you would submit.
You may want to take a look at this primer I wrote on OAuth 2.0 and the v2 Endpoint. It will help with understanding how OAuth works and the various calls required to obtain an access_token.
In order to retrieve profile information, including manager, from another user (a user other than the one who is authenticated) you need one of the following permission scopes:
User.Read.All
User.ReadWrite.All
Directory.Read.All
Directory.ReadWrite.All
Directory.AccessAsUser.All
You'll also need to obtain Admin Consent before you can use these scopes.
Once you have both the correct Scope and Admin Consent, you can request the profile for another user with /v1.0/users/{id}/manager.
I've created a web application that uses the OAuth authentication and universal connectors as explained in this tutorial, and started to fiddle around a little to add support for other providers like Yahoo and LinkedIn. So the authentication part works and users are created in the asp.net Membership provider. Also, all the providers return the accesstoken which I supposedly can use to retrieve more information regarding the user.
I'd really like to acquire the profile image, but it seems every provider has a different way of requesting this information. Twitter even describes a way to authorise every request by changing the HTTP header information.
Whilst reading this information on the websites of the various providers I was wondering whether this functionality isn't also already included somewhere in DotNetOpenAuth.AspNet or Microsoft.AspNet.Membership.OpenAuth implementation.
How can I use DotNetOpenAuth.AspNet and/or Microsoft.AspNet.Membership.OpenAuth to request the profile image of the loggedin user using the just acquired accesstoken?
UPDATE in response to Leo's answer
I use the following code to make a call on LinkedIn's API.
string accessToken = extraData["accesstoken"]; // Extra Data received from OAuth containing the accesstoken.
WebRequest request = WebRequest.Create("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token=" + accessToken);
using (WebResponse response = request.GetResponse())
{
// do something with response here.
}
Error message is "The remote server returned an error: (401) Unauthorized.".
What am I doing wrong?
The answer is simple...you can't use any of these. These are wrappers of OAuth and OAuth only specifies how you can authenticate a user. Now, to request the user's profile photo you will need to use the external provider's own API and you will need most likely a valid access token. So, you will need to use one of these implementations of OAuth to authenticate a user and the recieve an access token, store the access token somewhere (usually a cookie) and then use the access token to make sub-sequent calls to the provider's APIs. Examples and links....
Facebook's Graph API allows you to retrieve users profiles
https://developers.facebook.com/docs/graph-api/quickstart/
notice that all examples in the link above will require you to include the access token in a parameter named access_token, for example
https://graph.facebook.com/me?method=GET&format=json&suppress_http_code=1&access_token={your-access-token}
Google...
https://www.googleapis.com/oauth2/v3/userinfo?access_token={your-access-token}
LinkedIn...
https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token={your-access-token}
You can get more specific information from these providers' websites
Let me know if you have any other doubts I might be able to help you since I have implemented stuff like these before.
Cheers, Leo
I'm using dotNetOpenAuth to authorise against Google oAuth provider.
I'm a bit confused with the difference between the following:
consumerToken, consumerSecret, accessToken
From the Provider I get the accessToken for some user. Can keep I it forever? Or does it expires?
How can the code enable authorization without redirecting the user to the "allow access to my google data page" ?
Never expect have any expectations about lifespan of accessToken. At any time you can be given 403 HTTP error which should trigger on of the following in your app:
If you have a refreshToken, get a new accessToken without resource owner (end user) interaction
If not, ask user again to authorize your application
OAuth 1.0, which you're using, does not include a provision for predicting when an access token will expire, so you'll have to read Google's documentation for OAuth 1.0 access tokens to see how long they last.
How can the code enable authorization without redirecting the user to the "allow access to my google data page" ?
You don't. If you could do that, that would be a huge security leak. The user must authorize your app to access his/her data. Once you've obtained authorization once however, by storing the access token (and its secret) that you obtained you should be able to use it in the future and avoid the user authorization step (until the user revokes the token or it otherwise expires).
"AccessToken" in OAuth normally have relatively short expiration (i.e. in Facebook and Messenger case less than a day). If implementation supports it then "refreshToken" is the one you can keep longer (weeks/months range depending on provider).
According to the doc ( https://developers.google.com/accounts/docs/OAuth2 ) Google supports refresh tokens, so if you want to store token - it is the one.
Note that both accessToken and refreshToken represent very sensitive information (comparable to clear text user name and password), so please check out provider's recommendations and requirements on storing these information.