Is it possible to use OAuth 2.0 for Office365 SMTP? - oauth-2.0

I have an email application for sending emails that was written in house. We have set it with the option to use OAuth 2.0 with GMail (personal and business accounts) and Outlook.com accounts without issues.
We can also authentication with user ids and passwords but we prefer OAuth 2.0 as we don't save passwords anywhere that way.
We now have requests to do this for Office365 accounts.
I notice that the hello message on the Office365 smtp server (smtp.office365.com port 587) does not offer the XOAUTH2 option.
250-BY2PR0601CA0005.outlook.office365.com Hello [xx.xx.xx.xx]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH LOGIN
250-8BITMIME
250-BINARYMIME
250 CHUNKING
But, the SMTP server for outlook.com does:
250-BLU436-SMTP14.smtp.hotmail.com Hello [xx.xx.xx.xx]
250-TURN
250-SIZE 41943040
250-ETRN
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-8bitmime
250-BINARYMIME
250-CHUNKING
250-VRFY
250-AUTH LOGIN PLAIN XOAUTH2
250 OK
Is this possible to do with Office365? If not, can we point Office365 users to the outlook.com smtp server (smtp-mail.outlook.com) or are they totally different?
We'd rather not use the APIs just for sending emails if possible as the RESTful APIs for each provider will of course be quite different.
The reason for using OAuth 2.0 when sending email with an Office365 account is that we don't want to have to store passwords on our server. Also, if the user changes their password, we won't know unless they tell us or manually update it on our system side.
Using OAuth 2.0 this would solve this problem and allow the application to flow like with other email providers.

I really wanted this feature too. It would make Office365 apps that need to send mail that much easier!
I did some hunting, and found this which appears to be as close to an official answer as we are going to get (and the answer is a flat no).

Not sure if I'm missing something, but isn't this what you want? Looks like this was posted back in February. Interestingly, this article says that Oauth is supported for M365, but NOT for outlook.com users.
https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

I made one example using javax.Mail and OAuth for desktop application. It opens logon screen to get acccessToken. I followed multiple instructions so probably there are too many permissions and props in JavaMail but I succeeded to send mail.
My example program (Github)

PHP Example with OAuth2.
[On GitHub] (https://github.com/larsonnn/php_smtp_xoauth2_microsoft.php)
<?php
/* composer.json
"require": {
"phpmailer/phpmailer": "^6.6",
"league/oauth2-client": "^2.6",
"thenetworg/oauth2-azure": "^2.1"
}
*/
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\OAuth;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\PHPMailer;
use TheNetworg\OAuth2\Client\Provider\Azure;
require "vendor/autoload.php";
$mail = new PHPMailer(true);
$provider = new Azure([
'clientId' => '',
'clientSecret' => '',
"scopes" => ["https://outlook.office.com/SMTP.Send"],
"tenant" => "",
"defaultEndPointVersion" => Azure::ENDPOINT_VERSION_2_0,
]);
$mail->setOAuth(
new OAuth(
[
'provider' => $provider,
'clientId' => '',
'clientSecret' => '',
'refreshToken' => '',
'userName' => 'mymail#office_365_email.tld',
]
)
);
//Server settings
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->isSMTP();
$mail->Host = 'smtp.office365.com';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->AuthType = 'XOAUTH2';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->CharSet = PHPMailer::CHARSET_UTF8;
//Recipients
$mail->setFrom('mymail#office_365_email.tld', 'name');
$mail->addAddress('spam#example.tld', 'Spam');
//Content
$mail->Subject = 'Here is the subject';
$mail->Body = 'Hallo';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
$mail->send();

Related

Microsoft Graph suddenty returning "invalid_grant" for new users, still works fine locally

I've been using Microsoft Graph for months to get Calendar's read and write access for my app. Everything's was working fine until a few days ago where new user would get the following error message when following the authentification process for my Microsoft Graph app :
{"error":"invalid_grant","error_description":"Bad Request"}
The most surprising thing is that if I try the process locally (so using a local URL in "redirectUri"), it's working fine.
It's working fine on my local machine, there's no difference between the code on my local machine and the one in production except for the "RedirectURI" variable that is different.
Here's the code snippet for the authorisation url :
const OAUTH_APP_ID = "XXXXXXXXXXXXXX";
const OAUTH_APP_SECRET = "XXXXXXXXXXXXX";
const OAUTH_REDIRECT_URI = "XXXXXXXXXXXX";
const OAUTH_SCOPES= "openid profile offline_access user.read mailboxsettings.read calendars.readwrite";
const OAUTH_AUTHORITY= "https://login.microsoftonline.com/common";
const OAUTH_AUTHORIZE_ENDPOINT= "/oauth2/v2.0/authorize";
const OAUTH_TOKEN_ENDPOINT = "/oauth2/v2.0/token";
// Initialize the OAuth client
$oauthClient = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => MicrosoftGraphManager::OAUTH_APP_ID,
'clientSecret' => MicrosoftGraphManager::OAUTH_APP_SECRET,
'redirectUri' => MicrosoftGraphManager::OAUTH_REDIRECT_URI,
'urlAuthorize' => MicrosoftGraphManager::OAUTH_AUTHORITY.MicrosoftGraphManager::OAUTH_AUTHORIZE_ENDPOINT,
'urlAccessToken' => MicrosoftGraphManager::OAUTH_AUTHORITY.MicrosoftGraphManager::OAUTH_TOKEN_ENDPOINT,
'urlResourceOwnerDetails' => '',
'scopes' => MicrosoftGraphManager::OAUTH_SCOPES
]);
$authUrl = $oauthClient->getAuthorizationUrl();
// Save client state so we can validate in callback
$Session = $Request->getSession();
$Session->set("oauthState", $oauthClient->getState());
$extraParam = "&prompt=select_account";
$finalUrl = $authUrl.$extraParam;
If I set the "OAUTH_REDIRECT_URI" variable to my local environnment and try an authentification there, it's working fine, but if I change this url to the one in production and try the authentification on my production server, it's not working anymore.
Thanks!
This depends on which authorization flow you are using, if you want to access the other user calendar events or mails you need to use Client Credentials flow.

Google Drive API Response: but your computer or network may be sending automated queries

We are using the PHP SDK of Google spreadsheet API and Google Drive API. We provide customers to use Google Drive/Spreadsheet to backup their data stored on our local server. We use the server-side API.
We recently starting to receive errors from the server-side: 403 ERROR: We're sorry... but your computer or network may be sending automated queries. To protect our users, we can't process your request right now.
We are using offline access type, we are not reaching any quotas limits set in the Google API console. Can someone please help us with this issue, please?
Here is
$client = new Google_Client();
$client->setAuthConfig(__DIR__.'/credentials.json');
$client->setScopes([Google_Service_Sheets::SPREADSHEETS, Google_Service_Drive::DRIVE_FILE,
Google_Service_Drive::DRIVE_APPDATA]);
$client->setAccessType('offline');
$client->setApprovalPrompt("consent");
$client->setIncludeGrantedScopes(true);
$client->setAccessToken(json_decode($itemID["raw_token"],true));
$driveService = new Google_Service_Drive($client);
$optParams = array(
'q' => "mimeType='application/vnd.google-apps.folder' and name='XXXXX'",
'spaces' => 'drive',
'pageSize' => 10,
'fields' => 'nextPageToken, files(id, name)'
);
$results = $driveService->files->listFiles($optParams);
This 403 error sometimes is thrown during the above process.
Thanks!

Access Not Configured error while posting a message to google plus

I am using google-api-client gem to post message to google plus when a user logs in my application with his google plus credentials.
Below is my code.
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/installed_app'
keypath = Rails.root.join('config','poo-02a507f45ab4.p12').to_s
key = Google::APIClient::KeyUtils.load_from_pkcs12(keypath, 'notasecret')
client = Google::APIClient.new(
:application_name => 'my app',
:application_version => '1.0.0'
)
urlshortener = client.discovered_api('urlshortener', 'v1')
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => ['https://www.googleapis.com/auth/prediction','https://www.googleapis.com/auth/plus.stream.write','https://www.googleapis.com/auth/plus.login','https://www.googleapis.com/auth/urlshortener','https://www.googleapis.com/auth/plus.circles.write'],
:issuer => 'id#developer.gserviceaccount.com',
:signing_key => key)
client.authorization.fetch_access_token!
batch = Google::APIClient::BatchRequest.new do |result|
puts result.data
end
batch.add(:api_method => urlshortener.url.insert,:body_object => { 'longUrl' => 'https://www.facebook.co.in' })
client.execute(batch)
While trying this code its giving below error.
{\n \"domain\": \"usageLimits\",\n \"reason\": \"accessNotConfigured\",\n \"message\": \"Access Not Configured. The API is not enabled for your project, or there is a per-IP or per-Referer restriction configured on your API key and the request does not match these restrictions. Please use the Google Developers Console to update your configuration.\",\n
I have enabled google + API and contacts API in developers console.
Please help me to solve this problem.
The problem you are having is that you are using a service account.
The Google OAuth 2.0 system supports server-to-server interactions such as those between a web application and a Google service. This scenario is called a service account, which is an account that belongs to your application instead of to an individual end user. Your application calls Google APIs on behalf of the service account, so users aren't directly involved.
If you want to post on behalf of a user you will need to be using Oauth2, then you will be able to authenticate the user.
Update:
So that you are aware it is not possible to post to a users Google+ time line. the best you can do is post something called moments which isn't really the same thing. An issue request was made for this in 2011 I think they have yet to add this feature.

Cannot get oAuth2 Access Token for Google Analytics API

I am using Rails + Garb Gem (Sija Branch) + omniauth-google-oauth2 Gem and I can successfully authenticate with the Google Analytics API and extract data that our app is generating when using a user login, e.g.:
Garb::Session.login('USERNAME', '<PASSWORD>')
I can then use Garb to connect to the Analytics Profile I want and pull the data from it and display some charts on a webpage. This all works fine.
However, I want to use oAuth2 to authenticate with Analytics which is why I had to install the Sija branch of the Garb Gem from Github (it supports oAuth2) and I also installed the omniauth-google-oauth2 Gem. Now in theory I should be able to authenticate using the following code:
Garb::Session.access_token = access_token # an instance of OAuth2::Client
It's at this point that it gets a little hazy for me and I would greatly appreciate some guidance. Here's how far I have gotten:
1) I created a Project in the Google API console and turned on Analytics API under Services
2) This provided me with a Client ID and Client Secret
3) I came across this code which I could populate with the ID and Secret above:
client = OAuth2::Client.new(
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => '/o/oauth2/auth',
:token_url => '/o/oauth2/token'
})
4) Then there is the next bit of code:
response = OAuth2::AccessToken.new(
client,
STORED_TOKEN, {
refresh_token: STORED_REFRESH_TOKEN,
expires_at: STORED_EXPIRES_AT
})
5) and then in theory connect with:
Garb::Session.access_token = response
The problem I have is I don't have the token information in Point (4) above. It seems to me that with oAuth2 I need to do a "handshake" once and print out the return token values? Perhaps through Rails code which prints the values returned out and then paste the token values into a constant in the Rails app so that I can use them in the above code? I really am confused. As I mentioned earlier, the web app works fine using the user login authentication. All the web app is doing is authenticating with analytics, pulling down some data and drawing a chart. But I am stuck converting it over to oAuth2 as I just do not know how to get the Access Token that the Garb Gem is looking for. I should also note that this is not a public website with multiple users authenticating, this is a CMS website that is connecting to our own Analytics data.
I have seen some partial snippets of aspects of this but not a fully explained or working example. I would really appreciate any guidance and help with this question.
Many thanks in advance,
JR
I've soldiered through this over the last few weeks, so let me share what worked:
To use Oauth2 you need to get a 'refresh token' that you use to 're-authenticate' with google each time you make an API call. The steps for this are as follows:
1) Setup your account in the API console - https://code.google.com/apis/console/b/0/ (seems like you've done that well)
2) In your API account, make sure you have a redirect URI pointing back to your application:
http://some-url.com/auth/google_oauth2/callback
http://localhost:3000/auth/google_oauth2/callback
Note here that google won't let you call back to your local machine as 0.0.0.0:3000... so you'll need to use localhost explicitly
3) In your route file, tie that redirect url to an action in the controller where you're going to create the project or authentication
match '/auth/:provider/callback' => 'authentications#create'
The ':provider' simply lets you match on multiple types of oauth, but you could just put 'google_oauth2' there as well.
4) Now create that action in your controller
def create
auth = request.env["omniauth.auth"]
params = request.env["omniauth.params"]
project = Project.find(params['project_id'])
Authentication.create(:project_id => project.id, :provider => auth['provider'], :uid => auth['uid'], :access_token => auth['credentials']['refresh_token'])
flash[:notice] = "Authentication successful."
redirect_to owner_view_project_path(project)
end
5) The controller action should retrieve the relevant fields from the response object (details of response object here: https://github.com/zquestz/omniauth-google-oauth2) - in particular, you need to get the 'refresh_token' and save that to your project or authentication object - if you haven't added an 'access_token' attribute to the desired object, go do that now with a migration, then start saving the refresh token to that attribute
6) Now when you're ready to call that particular authentication and get API data for it, you can load up that object where you saved the access token, and use that to get a new session with the google API as follows:
#authentication = Authentications.find(params[:id])
client = OAuth2::Client.new GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => "/o/oauth2/auth",
:token_url => "/o/oauth2/token",
}
response = OAuth2::AccessToken.from_hash(client, :refresh_token => #authentication.access_token).refresh!
Garb::Session.access_token = response
#profiles = Garb::Management::Profile.all
What this code did was create an OAuth2 access token (response) by specifying the client and then a refresh_token, then calling 'refresh!' to get a refreshed access token... then use that access token to establish your Garb session, then call down all the profiles for a given account using the Gard::Management::Profile.all
Hope this helps - let me know if you have questions!
Just a note on what worked for me in:
For steps 3, 4 & 5 I used cURL instead to retrieve the Access/Refresh token. Step 6 is then the same for me (using the Sija branch of the Garb Gem). So using cURL:
Using the details associated with your Google app POST the following using cURL:
curl --data "code=<APP_CODE>&redirect_uri=http://localhost:3000/oauth2callback&client_id=<CLIENT_ID>.apps.googleusercontent.com&scope=&client_secret=<CLIENT_SECRET>&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token
The response takes the form:
{
"access_token" : "<ACCESS_TOKEN>",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "<REFRESH_TOKEN>"
}
which you can plug into the Garb Gem as per part 6.
The answer by #CamNorgate is valid.
If you don't have a "refresh_token" back from Omniauth on the callback make sure you are correctly initializing :google_oauth2
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{ :scope=>"https://www.google.com/m8/feeds, https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile",
:approval_prompt=>"force", access_type="offline"
}
end
Make sure to include :approval_prompt=>"force", access_type="offline" in order for the refresh_token to be sent back. The refresh_token is only provided on the first authorization from the user.

Twitter Streaming API with OAuth?

I've been stuck on this for awhile. Does anyone know how to authenticate the Twitter Streaming API requests using OAuth? As of right now I'm authenticating via Basic Authentication, and I would like to completely switch over to OAuth. Also, I'm using Ruby on Rails if that helps.
Thanks
Connecting to the Twitter Streaming API via OAuth is done much the same as connecting via the REST API. Assuming you've already negotiated an access token, you sign and issue the request using the same signing algorithm as for a REST request. With the Streaming API, it's best to use header-based OAuth rather than query-string based.
Here's an example of a signed OAuth-based request for the sample end point:
GET http://stream.twitter.com/1/statuses/sample.json
Signature Base String example:
GET&http%3A%2F%2Fstream.twitter.com%2Fstatuses%2Fsample.json&oauth_consumer_key%3Dri8JxYK2ddwSV5xIUfNNvQ%26oauth_nonce%3DUJb0f3nHhFQkpkWkJzxnFT65xX1TZeuGjww6Q2XWs4%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1306947138%26oauth_token%3D819797-torCkTs0XK7H2Y2i1ee5iofqkMC4p7aayeEXRTmlw%26oauth_version%3D1.0
Authorization Header after signing:
Authorization: OAuth oauth_consumer_key="ri8JxYK2ddwSV5xIUfNNvQ", oauth_nonce="UJb0f3nHhFQkpkWkJzxnFT65xX1TZeuGjww6Q2XWs4", oauth_signature="bN14zlBIdCZCSl9%2B8UV8dB2VWjI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1306947138", oauth_token="819797-torCkTs0XK7H2Y2i1ee5iofqkMC4p7aayeEXRTmlw", oauth_version="1.0"
Matt Harris has some sample code in PHP demonstrating connecting to the streaming API via OAuth: https://github.com/themattharris/tmhOAuth/blob/master/examples/streaming.php
After you register your application on http://dev.twitter.com this is how it's done in Perl:
#!/usr/bin/perl
use strict;
use AnyEvent::Twitter::Stream;
if ($ENV{FIREHOSE_SERVER}) {
$AnyEvent::Twitter::Stream::STREAMING_SERVER = $ENV{FIREHOSE_SERVER};
}
my $done = AE::cv;
binmode STDOUT, ":utf8";
my $streamer = AnyEvent::Twitter::Stream->new(
consumer_key => 'KEY',
consumer_secret => 'SECRET',
token => 'TOKEN',
token_secret => 'TOKEN SECRET',
method => "filter",
track => "KEYWORDS TO TRACK",
on_tweet => sub {
# CUSTOM CODE HERE
},
on_error => sub {
my $error = shift;
warn "ERROR: $error";
$done->send;
},
on_eof => sub {
$done->send;
},
);
$done->recv;
Try the OmniAuth gem which supports many external providers https://github.com/intridea/omniauth
You should use this gem : Tweetstream which sits on top of em-twitter

Resources