I have an elevated development account in Twitter.
I'm testing Tweetinvi with this code:
private async Task ConnectToTwitter()
{
// we create a client with your user's credentials
var userClient = new TwitterClient(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret);
toolStripStatusLabel1.Text = "Connected";
// request the user's information from Twitter API
var user = await userClient.Users.GetAuthenticatedUserAsync();
labelUserName.Text = user.Name;
// publish a tweet
var tweet = await userClient.Tweets.PublishTweetAsync("Hello tweetinvi world!");
Console.WriteLine("You published the tweet : " + tweet);
}
And I get this error:
Reason : Unauthorized
Code : 401
Date : 29/05/2022 15:25:49 +02:00
URL : https://api.twitter.com/1.1/statuses/update.json?status=Hello%20tweetinvi%20world%21&tweet_mode=extended
Twitter documentation description : Unauthorized - Authentication credentials were missing or incorrect.
On this line of code:
var tweet = await userClient.Tweets.PublishTweetAsync("Hello tweetinvi world!");
How can I add the credentials to the PublishTweetAsync call?
Or maybe, there is another way to publish a tweet with credentials without using PublishTweetAsync?
UPDATE
Due to the answer of Andy Piper, I have to say that the code compiles, so all the arguments for TwitterClient has a value (of course, I'm not going to share them here), and I can get the user here:
var user = await userClient.Users.GetAuthenticatedUserAsync();
I have also enabled this:
But I don't know what to put in the Callback URI.
The answer is in your codeā¦
// we create a client with your user's credentials
var userClient = new TwitterClient(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret);
You need to have the values defined in your code to pass in to the TwitterClient instance. You also need to ensure that you have an app with access to the v1.1 API (elevated access) to use this code, since it will not work with v2.
Finally, you should check that your app has read and write permissions and that your access token and secret were generated with these permissions.
Related
In our application, we need to send notifications to users by email for various event triggers.
I'm able to send email if I send as "Me" the current user, but trying to send as another user account returns an error message and I'd prefer it if notifications didn't come users' themselves and may contain info we don't want floating around in Sent folders.
What works:
await graphClient.Me.SendMail(email, SaveToSentItems: false).Request().PostAsync();
What doesn't work:
string FromUserEmail = "notifications#contoso.com";
await graphClient.Users[FromUserEmail].SendMail(email, SaveToSentItems: false).Request().PostAsync();
Also tried using the user object id directly:
await graphClient.Users["cd8cc59c-0815-46ed-aa45-4d46c8a89d72"].SendMail(email, SaveToSentItems: false).Request().PostAsync();
My application has permissions for the Graph API to "Send mail as any user" enabled and granted by the owner/administrator.
The error message returned by the API:
Code: ErrorFolderNotFound Message: The specified folder could not be
found in the store.
I thought this error might have been because the notifications account didn't have a sent folder, so I set the SaveToSentItems value to false, but I still get the same error.
Are there any settings I need to check on the account itself to allow the app to send mail on this account or should this work?
I have checked out the documentation here:
https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/user_sendmail
Which appears to support what I'm trying to do, but doesn't reference any folder except for the sent items folder which I'm telling the API not to save to anyway.
We aren't intending to impersonate any actual user here, just send notification emails from within the app from this specific account (which I know is technically impersonation, but not of a real entity).
So like Schwarzie2478 we used a noreply#ourcompany.com address. But our AD is federated which means you can't use Username\Password auth and we didn't want to use the Application Mail.Send permission since it literally can send as anyone and there is no way IT Security would let that fly. So we used Windows Authentication instead.
This requires that you grant consent to the app to use the mail.send and user.read delegate permissions by going to https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read%20mail.send and logging in with the windows user that the app will run as.
More info on using windows auth here: https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Integrated-Windows-Authentication
// method call
var t = SendEmailUsingGraphAPI();
t.Wait();
// method
static async Task<Boolean> SendEmailUsingGraphAPI() {
// AUTHENTICATION
var tenantID = "YOUR_TENANT_ID"; //azure ad tenant/directory id
var clientID = "YOUR_APPS_CLIENT_ID"; // registered app clientID
var scopes = "user.read mail.send"; // DELEGATE permissions that the request will need
string authority = $"https://login.microsoftonline.com/{tenantID}";
string[] scopesArr = new string[] { scopes };
try {
IPublicClientApplication app = PublicClientApplicationBuilder
.Create(clientID)
.WithAuthority(authority)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any()) {
result = await app.AcquireTokenSilent(scopesArr, accounts.FirstOrDefault())
.ExecuteAsync();
}
else {
// you could acquire a token by username/password authentication if you aren't federated.
result = await app.AcquireTokenByIntegratedWindowsAuth(scopesArr)
//.WithUsername(fromAddress)
.ExecuteAsync(CancellationToken.None);
}
Console.WriteLine(result.Account.Username);
// SEND EMAIL
var toAddress = "EMAIL_OF_RECIPIENT";
var message = "{'message': {'subject': 'Hello from Microsoft Graph API', 'body': {'contentType': 'Text', 'content': 'Hello, World!'}, 'toRecipients': [{'emailAddress': {'address': '" + result.Account.Username + "'} } ]}}";
var restClient = new RestClient("https://graph.microsoft.com/v1.0/users/" + result.Account.Username + "/sendMail");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + result.AccessToken);
request.AddParameter("", message, ParameterType.RequestBody);
IRestResponse response = restClient.Execute(request);
Console.WriteLine(response.Content);
}
catch (Exception e) {
Console.WriteLine(e.Message);
throw e;
}
return true;
}
Whenever you are using delegated permissions (i.e. when a user is logged in), even though your admin has consented to the Mail.Send.Shared, it does NOT grant access to all mailboxes in the tenant. These OAuth permissions do not override the permissions (and restrictions) in place for the user.
If the user is not already configured with permissions to be able to "Send As" the notifications#contoso.com user, then you'll see this error.
To make it work, you'd need to actually grant "Send As" rights to all users that will be using your application.
This is a subtle thing, and granted it's a bit confusing. In the Azure portal, the permissions have slightly different descriptions, depending on if you're looking at the Application Permissions or the Delegated Permissions.
Application: Send mail as any user
Delegated: Send mail on behalf of others
Since you're using delegated, the permission doesn't allow you to send as any user, only send on behalf of any folks that the logged on user has rights to send as.
Another approach you could use here to avoid having to grant these rights to all users (which would allow them to send via Outlook, etc.) would be to have your backend app use the client credentials flow to get an app-only token. In that case, the app itself would have the permission to send as any user.
I don't know what others will have done for this, but I contacted Microsoft about this exact scenario: I want to send a mail as a fixed user ( noreply#mycompany.com) which has a mailbox in Azure. I want to send this mail from different applications or services.
The person there told me that sending a mail with no user logging in, is only possible with an delegated user token.
So we configured our application as an Native application in Azure like for mobile apps. Logging in for this application with the technical user during a setup phase gives me a delegated user token for that specific user which can be stored in a mailing service or component. This token does not expire ( at least not until the security changes of the user like password or something) and can be used to call the graph api to send mails when you give permission for this account to be sending mails from.
Next to that we even associated other shared mailboxes to this accounts to be able to send mails for those mailboxes too.
Documentation:
First You need a native app registration in Azure ( not an Web API):
https://learn.microsoft.com/en-us/azure/active-directory/develop/native-app
This app only requires an one-time login and approval from an user to get a token which can represent that user indefinitly. We set up a mail user account to be used for this. That token is then used to get access token to Graph Api for sending mails and such
Token Handling example:
https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/token-cache-serialization
With an identitytoken stored ( usually a .cache file somewhere) you can request an accesstoken:
Identity Client:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.identity.client.publicclientapplication?view=azure-dotnet
_clientApp = new PublicClientApplication(ClientId, "https://login.microsoftonline.com/{xxx-xxx-xx}, usertoken,...
authResult = await _clientApp.AcquireTokenSilentAsync(scopes,...
private static string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
//Set the scope for API call to user.read
private static string[] scopes = new string[] { "user.read", "mail.send" };
private const string GraphApi = "https://graph.microsoft.com/";
var graphclient = new GraphServiceClient($"{GraphApi}/beta",
new DelegateAuthenticationProvider(
(requestMessage) =>
{
// inject bearer token for auth
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
return Task.FromResult(0);
}));
var sendmail = graphclient.Users[User].SendMail(mail), true);
try
{
await sendmail.Request().PostAsync();
}
catch (Exception e)
{
Hello I am trying to get user info like name and address in an Xamarin app using Azure AD B2C for authentication.
So far I've gotten the authentication working fine
public async Task<MobileServiceUser> LoginAsync(MobileServiceClient client, MobileServiceAuthenticationProvider provider)
{
try
{
//login and save user status
var user = await client.LoginAsync(Forms.Context, provider);
Settings.AuthToken = user?.MobileServiceAuthenticationToken ?? string.Empty;
Settings.UserId = user?.UserId ?? string.Empty;
return user;
}
catch (Exception e)
{
}
return null;
}
However I would like to know how to get the user's name and birthday. I haven't been able to find a clear course of action for that.
You do not explicitly get this information using the MobileService SDK. Check out the complete documentation about App Service Authentication/Authorization here.
You will reach the point where it mentions:
Your application can also obtain additional user details through an
HTTP GET on the /.auth/me endpoint of your application. A valid token
that's included with the request will return a JSON payload with
details about the provider that's being used, the underlying provider
token, and some other user information. The Mobile Apps server SDKs
provide helper methods to work with this data.
So, in your Xamarin, after the user is successfully authentication, you have to explicitly make a HTTP GET request to /.auth/me and parse the result to get all information about the logged-in user.
Not sure how to do this in Xamarin, but here is how to do it in C# UWP (Universal Windows Platform):
var url = App.MobileService.MobileAppUri + "/.auth/me";
var clent = new Windows.Web.Http.HttpClient();
clent.DefaultRequestHeaders.Add("X-ZUMO-AUTH", this.user.MobileServiceAuthenticationToken);
var userData = await clent.GetAsync(new Uri(url));
at the of this code execution, the userData varibale will be a JSON srting with all user's claims.
I'm trying to use the Google Admin Settings API with a Service Account with no success from a C# Console application.
From what I've understood, I first have to get an OAuth token. I've tried 2 methods successfully for this: using Google.Apis.Auth.OAuth2.ServiceAccountCredentials or by creating manually the JWT assertion.
But when I call an Admin Settings API with the OAuth token (maximumNumberOfUsers for instance), I always get a 403 error with " You are not authorized to perform operations on the domain xxx" message.
I downloaded GAM as the author calls this API too so that I can compose the same HTTP requests. Like explained in GAM wiki, I followed all the steps to create a new Service Account and a new OAuth Client ID so that I can be sure it's not a scope issue. I also activated the debug mode like proposed by Jay Lee in this thread. Like explained in the thread comments, it still doesn't work with my OAuth token but the call to the API succeeds with GAM OAuth token.
So it seems it's related to the OAuth token itself. An issue I get while creating the OAuth token is that I can't specify the "sub" property (or User for ServiceAccountCredentials). If I add it, I get a 403 Forbidden response with "Requested client not authorized." as error_description while generating the token i.e. before calling the API. So maybe it is the issue but I don't see how to fix it as I use an Admin email.
Another possibility is that this API needs the OAuth Client credentials as GAM requires 2 different types of credentials, Service Account and OAuth Client. As I only can use Service Account credentials in my project, I'm afraid I will be stuck if it is the case...
I don't see other options and I'm stuck with both, so any help appreciated. Thanks!
My code:
public static string GetEnterpriseUsersCount()
{
string domain = MYDOMAIN;
string certPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
certPath = certPath.Substring(0, certPath.LastIndexOf("\\") + 1) + "GAMCreds.p12";
var certData = File.ReadAllBytes(certPath);
X509Certificate2 privateCertificate = new X509Certificate2(certData, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/domain/" },
User = ADMIN_EMAIL
}.FromCertificate(privateCertificate));
Task<bool> oAuthRequest = credential.RequestAccessTokenAsync(new CancellationToken());
oAuthRequest.Wait();
string uri = string.Format("https://apps-apis.google.com/a/feeds/domain/2.0/{0}/general/maximumNumberOfUsers", domain);
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
if (request != null)
{
request.Method = "GET";
request.Headers.Add("Authorization", string.Format("Bearer {0}", credential.Token.AccessToken));
// Return the response
using (WebResponse response = request.GetResponse())
{
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
}
return null;
}
Edit: I focused on scopes like advised by Jay Lee below and it appears that the missing scope was 'https://www.googleapis.com/auth/admin.directory.domain'. However, nowhere is this written in Admin Settings API documentation page. At least, I didn't find it. 'https://apps-apis.google.com/a/feeds/domain/' is necessary too but I already added it to the list of allowed scopes. Thanks Jay!
Edit 2: I also updated the source code so that it can help in the future.
You need to grant your service account's client ID access to the scopes for admins settings API. Follow the Drive domain wide delegation instructions except sub in the correct correct scope. Then you can set sub= without an error.
I am trying to download a user's mailbox using the Email Audit API. I am getting a 403 Forbidden response to this code (the error occurs on the last line, the call to the UploadPublicKey method):
var certificate = new X509Certificate2(System.Web.HttpRuntime.AppDomainAppPath + "key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/compliance/audit/" }
}.FromCertificate(certificate));
credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();
DebugLabel.Text = credential.Token.AccessToken;
var requestFactory = new GDataRequestFactory("My App User Agent");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
AuditService aserv = new AuditService(strOurDomain, "GoogleMailAudit");
aserv.RequestFactory = requestFactory;
aserv.UploadPublicKey(strPublicKey);
I have created the service account in the Developers Console and granted the Client ID access to https://apps-apis.google.com/a/feeds/compliance/audit/ in the Admin console.
Seems to me like the account should have all the permissions it needs, yet it doesn't. Any idea what I am missing?
OK, so I gave up on trying to make it work with a service account even though that is what Google's documentation would lead you to believe is the correct way to do it. After emailing Google support, I learned I could just use OAuth2 for the super user account that created the application on the developer's console.
So then I worked on getting an access token for offline access (a refresh token) by following the process outlined here:
Youtube API single-user scenario with OAuth (uploading videos)
and then taking that refresh token and using it with this code:
public static GOAuth2RequestFactory RefreshAuthenticate(){
OAuth2Parameters parameters = new OAuth2Parameters(){
RefreshToken = "<YourRefreshToken>",
AccessToken = "<AnyOfYourPreviousAccessTokens>",
ClientId = "<YourClientID>",
ClientSecret = "<YourClientSecret>",
Scope = "https://apps-apis.google.com/a/feeds/compliance/audit/",
AccessType = "offline",
TokenType = "refresh"
};
OAuthUtil.RefreshAccessToken(parameters);
return new GOAuth2RequestFactory(null, "<YourApplicationName>", parameters);
}
which is code from here https://stackoverflow.com/a/23528629/5215904 (Except I changed the second to last line... for whatever reason the code shared did not work until I made that change).
So there I was finally able to get myself an access token that would allow me access to the Email Audit API. From there everything was a breeze once I stopped trying to mess around with a service account.
I have worked with OAuth before (working with Twitter and PHP) and it was simple. I am trying to get OAuth to work with the EverNote API sample https://github.com/evernote/evernote-sdk-csharp (because, as they say, "Real applications authenticate with Evernote using OAuth"). I looked at these:
Simple C# Evernote API OAuth example or guide?
https://github.com/sethhitch/csharp-oauth-sample
http://blog.stevienova.com/2008/04/19/oauth-getting-started-with-oauth-in-c-net/
But, I still don't know how to do this... This is my code:
// Real applications authenticate with Evernote using OAuth, but for the
// purpose of exploring the API, you can get a developer token that allows
// you to access your own Evernote account. To get a developer token, visit
// https://sandbox.evernote.com/api/DeveloperToken.action
String authToken = "myAuthCode";
if (authToken == "your developer token") {
Console.WriteLine("Please fill in your developer token");
Console.WriteLine("To get a developer token, visit https://sandbox.evernote.com/api/DeveloperToken.action");
return;
}
How can I add OAuth to this to get my authToken?
Thank you.
Check this sample project : http://discussion.evernote.com/topic/30584-here-is-a-net-oauth-assembly/ . I think this will help you to understand how oauth works.
For anyone trying to get this to work in MVC, I was playing around with Evernote, OpenAuth and C# this morning and managed to get it all working. I have put together a blog post / library explaining the experience and outlining how to do it with MVC here - http://www.shaunmccarthy.com/evernote-oauth-csharp/ - it uses the AsyncOAuth library: https://github.com/neuecc/AsyncOAuth
I wrote a wrapper around AsyncOAuth that you might find useful here: https://github.com/shaunmccarthy/AsyncOAuth.Evernote.Simple
One prickly thing to be aware of - the Evernote Endpoints (/oauth and /OAuth.action) are case sensitive
// Download the library from https://github.com/shaunmccarthy/AsyncOAuth.Evernote.Simple
// Configure the Authorizer with the URL of the Evernote service,
// your key, and your secret.
var EvernoteAuthorizer = new EvernoteAuthorizer(
"https://sandbox.evernote.com",
"slyrp-1234", // Not my real id / secret :)
"7acafe123456badb123");
// First of all, get a request token from Evernote - this causes a
// webrequest from your server to Evernote.
// The callBackUrl is the URL you want the user to return to once
// they validate the app
var requestToken = EvernoteAuthorizer.GetRequestToken(callBackUrl);
// Persist this token, as we are going to redirect the user to
// Evernote to Authorize this app
Session["RequestToken"] = requestToken;
// Generate the Evernote URL that we will redirect the user to in
// order to
var callForwardUrl = EvernoteAuthorizer.BuildAuthorizeUrl(requestToken);
// Redirect the user (e.g. MVC)
return Redirect(callForwardUrl);
// ... Once the user authroizes the app, they get redirected to callBackUrl
// where we parse the request parameter oauth_validator and finally get
// our credentials
// null = they didn't authorize us
var credentials = EvernoteAuthorizer.ParseAccessToken(
Request.QueryString["oauth_verifier"],
Session["RequestToken"] as RequestToken);
// Example of how to use the credential with Evernote SDK
var noteStoreUrl = EvernoteCredentials.NotebookUrl;
var noteStoreTransport = new THttpClient(new Uri(noteStoreUrl));
var noteStoreProtocol = new TBinaryProtocol(noteStoreTransport);
var noteStore = new NoteStore.Client(noteStoreProtocol);
List<Notebook> notebooks = client.listNotebooks(EvernoteCredentials.AuthToken);
You can also try the OAuth library found here : https://code.google.com/p/devdefined-tools/wiki/OAuth and follow the steps mentioned here.
The simple code to add is:
EvernoteOAuth oauth = new EvernoteOAuth(EvernoteOAuth.HostService.Sandbox, myConsumerKey, myConsumerSecret);
string errResponse = oauth.Authorize();
if (errResponse.Length == 0)
{
Console.WriteLine(string.Format("Token: {0}\r\n\r\nExpires: {1}\r\n\r\nNoteStoreUrl: {2}\r\n\r\nUserId: {3}\r\n\r\nWebApiUrlPrefix: {4}", oauth.Token, oauth.Expires, oauth.NoteStoreUrl, oauth.UserId, oauth.WebApiUrlPrefix));
}
else
{
Console.WriteLine("A problem has occurred in attempting to authorize the use of your Evernote account: " + errResponse);
}
You will need to use this assembly:
using EvernoteOAuthNet;
Available here:
http://www32.zippyshare.com/v/98249023/file.html