Can not get signed in email using Office 365 REST API - outlook

I followed this post http://dev.office.com/code-samples-detail/2142 and Ruby to get user's email address. Here is the code:
# Parses an ID token and returns the user's email
def get_email_from_id_token(id_token)
# JWT is in three parts, separated by a '.'
token_parts = id_token.split('.')
# Token content is in the second part
encoded_token = token_parts[1]
# It's base64, but may not be padded
# Fix padding so Base64 module can decode
leftovers = token_parts[1].length.modulo(4)
if leftovers == 2
encoded_token += '=='
elsif leftovers == 3
encoded_token += '='
end
# Base64 decode (urlsafe version)
decoded_token = Base64.urlsafe_decode64(encoded_token)
# Load into a JSON object
jwt = JSON.parse(decoded_token)
# Email is in the 'preferred_username' field
email = jwt['preferred_username']
end
This function worked very well, I can get user's email address. But today, this function still works without error but the JSON I got not contain user's email address anymore.
Could someone help me? I want to get user's email address. Thank you !

Azure deployed a breaking change to the v2 app model, and you don't get user info by default anymore.
You can read all about it here: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-preview-oidc-changes/, but to summarize:
The openid scope used to give you basic profile info for the user.
That wasn't in line with the OpenID standard
Azure changed to require that you request the profile scope to get access to that information
For that sample, find this bit:
# Scopes required by the app
SCOPES = [ 'openid',
'https://outlook.office.com/mail.read' ]
And change it to:
# Scopes required by the app
SCOPES = [ 'openid',
'profile',
'https://outlook.office.com/mail.read' ]

Please add profile and email in your scope :
SCOPES = [ 'openid',
'profile',
'email',
'https://outlook.office.com/mail.read' ]

Related

where the 'attr' value of the validate of SocialLoginSerializer is output in django rest framework

I wonder where the attrs of validate in drf's SocialLoginSerializer are expressed.
Actually, I want to change the response values ​​when I do social login,
but I don't know where to change them.
Please help me
class SocialLoginSerializer(serializers.Serializer):
...
def validate(self, attrs):
...
if not login.is_existing:
# We have an account already signed up in a different flow
# with the same email address: raise an exception.
# This needs to be handled in the frontend. We can not just
# link up the accounts due to security constraints
if allauth_settings.UNIQUE_EMAIL:
# Do we have an account already with this email address?
account_exists = get_user_model().objects.filter(
email=login.user.email,
).exists()
if account_exists:
raise serializers.ValidationError(
_('User is already registered with this e-mail address.'),
)
login.lookup()
login.save(request, connect=True)
return attrs

MIP SDK fails to protect files

I'm using MIP file sample command line interface to apply labelling.
When trying to apply a label that has protection set, i got "Label requires ad-hoc protection, but protection has not yet been set" error.
Therefore, I tried protecting the file using "--protect" option and got the following error message:
"Something bad happened: The service didn't accept the auth token. Challenge:['Bearer resource="https://aadrm.com", realm="", authorization="https://login.windows.net/common/oauth2/authorize"'], CorrelationId=ce732e4a-249a-47ec-a7c2-04f4d68357da, CorrelationId.Description=ProtectionEngine, CorrelationId=6ff992dc-91b3-4610-a24d-d57e13902114, CorrelationId.Description=FileHandler"
This is my auth.py file:
def main(argv):
client_id = str(argv[0])
tenant_id = str(argv[1])
secret = str(argv[2])
authority = "https://login.microsoftonline.com/{}".format(tenant_id)
app = msal.ConfidentialClientApplication(client_id, authority=authority, client_credential=secret)
result = None
scope = ["https://psor.o365syncservice.com/.default"]
result = app.acquire_token_silent(scope, account=None)
if not result:
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
result = app.acquire_token_for_client(scopes=scope)
if "access_token" in result:
sys.stdout.write(result['access_token'])
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bug
if __name__ == '__main__':
main(sys.argv[1:])
I tried to change the scope to ["https://aadrm.com/.default"] and then I was able to protect the file, but when I try getting the file status or try applying label on it I get the same error message with bad auth token.
These are the permissions as configured in azure portal:
Thank you
I think that scope you have is incorrect: https://psor.o365syncservice.com/.default
It should be https://syncservice.o365syncservice.com/.default.
A good way to handle this is to just append .default to whatever resource the AcquireToken() call gets in the resource param. Something like this.

Generate expiring activator token or a key hash in rails manually

I'm trying to verify a link that will expire in a week. I have an activator_token stored in the database, which will be used to generate the link in this format: http://www.example.com/activator_token. (And not activation tokens generated by Devise or Authlogic.)
Is there a way to make this activator token expire (in a week or so) without comparing with updated_at or some other date. Something like an encoded token, which will return nil when decoded after a week. Can any existing modules in Ruby do this? I don't want to store the generated date in the database or in an external store like Redis and compare it with Time.now. I want it to be very simple, and wanted to know if something like this already exists, before writing the logic again.
What you want to use is: https://github.com/jwt/ruby-jwt .
Here is some boilerplate code so you can try it out yourself.
require 'jwt'
# generate your keys when deploying your app.
# Doing so using a rake task might be a good idea
# How to persist and load the keys is up to you!
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key
# do this when you are about to send the email
exp = Time.now.to_i + 4 * 3600
payload = {exp: exp, discount: '9.99', email: 'user#example.com'}
# when generating an invite email, this is the token you want to incorporate in
# your link as a parameter
token = JWT.encode payload, rsa_private, 'RS256'
puts token
puts token.length
# this goes into your controller
begin
#token = params[:token]
decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' }
puts decoded_token.first
# continue with your business logic
rescue JWT::ExpiredSignature
# Handle expired token
# inform the user his invite link has expired!
puts "Token expired"
end

Obtaining a Facebook auth token for a command-line (desktop) application

I am working for a charity which is promoting sign language, and they want to post a video to their FB page every day. There's a large (and growing) number of videos, so they want to schedule the uploads programmatically. I don't really mind what programming language I end up doing this in, but I've tried the following and not got very far:
Perl using WWW::Facebook::API (old REST API)
my $res = $client->video->upload(
title => $name,
description => $description,
data => scalar(read_file("videos/split/$name.mp4"))
);
Authentication is OK, and this correctly posts a facebook.video.upload method to https://api-video.facebook.com/restserver.php. Unfortunately, this returns "Method unknown". I presume this is to do with the REST API being deprecated.
Facebook::Graph in Perl or fb_graph gem in Ruby. (OAuth API)
I can't even authenticate. Both of these are geared towards web rather than desktop applications of OAuth, but I think I ought to be able to do:
my $fb = Facebook::Graph->new(
app_id => "xxx",
secret => "yyy",
postback => "https://www.facebook.com/connect/login_success.html"
);
print $fb->authorize->extend_permissions(qw(publish_stream read_stream))->uri_as_string;
Go to that URL in my browser, capture the code parameter returned, and then
my $r = $fb->request_access_token($code);
Unfortunately:
Could not fetch access token: Bad Request at /Library/Perl/5.16/Facebook/Graph/AccessToken/Response.pm line 26
Similarly in Ruby, using fb_graph,
fb_auth = FbGraph::Auth.new(APP_ID, APP_SECRET)
client = fb_auth.client
client.redirect_uri = "https://www.facebook.com/connect/login_success.html"
puts client.authorization_uri(
:scope => [:publish_stream, :read_stream]
)
Gives me a URL which returns a code, but running
client.authorization_code = <code>
FbGraph.debug!
access_token = client.access_token!
returns
{
"error": {
"message": "Missing client_id parameter.",
"type": "OAuthException",
"code": 101
}
}
Update: When I change the access_token! call to access_token!("foobar") to force Rack::OAuth2::Client to put the identifier and secret into the request body, I get the following error instead:
{
"error": {
"message": "The request is invalid because the app is configured as a desktop app",
"type": "OAuthException",
"code": 1
}
}
How am I supposed to authenticate a desktop/command line app to Facebook using OAuth?
So, I finally got it working, without setting up a web server and doing a callback. The trick, counter-intuitively, was to turn off the "Desktop application" setting and not to request offline_access.
FaceBook::Graph's support for posting videos doesn't seem to work at the moment, so I ended up doing it in Ruby.
fb_auth = FbGraph::Auth.new(APP_ID, APP_SECRET)
client = fb_auth.client
client.redirect_uri = "https://www.facebook.com/connect/login_success.html"
if ARGV.length == 0
puts "Go to this URL"
puts client.authorization_uri(:scope => [:publish_stream, :read_stream] )
puts "Then run me again with the code"
exit
end
if ARGV.length == 1
client.authorization_code = ARGV[0]
access_token = client.access_token! :client_auth_body
File.open("authtoken.txt", "w") { |io| io.write(access_token) }
exit
end
file, title, description = ARGV
access_token = File.read("authtoken.txt")
fb_auth.exchange_token! access_token
File.open("authtoken.txt", "w") { |io| io.write(fb_auth.access_token) }
me = FbGraph::Page.new(PAGE_ID, :access_token => access_token)
me.video!(
:source => File.new(file),
:title => title,
:description => description
)
Problem is in your case that for OAuth you'll need some endpoint URL which is publicly reachable over the Internet for Facebook servers, which can be a no-go for normal client PCs, or a desktop application which is capable of WebViews (and I assume, command line isn't).
Facebook states at https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login that you can build a desktop client login flow, but only via so-called WebViews. Therefore, you'd need to call the OAuth endpoint like this:
https://www.facebook.com/dialog/oauth?client_id={YOUR_APP_ID}&redirect_uri=https://www.facebook.com/connect/login_success.html&response_type=token&scope={YOUR_PERMISSION_LIST}
You then have to inspect the resulting redirected WebView URL as quoted:
When using a desktop app and logging in, Facebook redirects people to
the redirect_uri mentioned above and places an access token along with
some other metadata (such as token expiry time) in the URI fragment:
https://www.facebook.com/connect/login_success.html#access_token=ACCESS_TOKEN...
Your app needs to detect this redirect and then read the access token out of the URI using the mechanisms provided by the OS and development framework you are using.
If you want to do this in "hacking mode", I'd recommend to do the following.:
As you want to post to a Page, get a Page Access Token and store it locally. This can be done by using the Graph Explorer at the
https://developers.facebook.com/tools/explorer?method=GET&path=me%2Faccounts
endpoint. Remember to give "manage_pages" and "publish_actions" permissions.
Use cURL (http://curl.haxx.se/docs/manpage.html) to POST the videos to the Graph API with the Access Token and the appropriate Page ID you acquired in step 1 like the following:
curl -v -0 --form title={YOUR_TITLE} --form
description={YOUR_DESCRIPTION} --form source=#{YOUR_FULL_FILE_PATH}
https://graph-video.facebook.com/{YOUR_PAGE_ID}/videos?access_token={YOUR_ACCESS_TOKEN}
References:
https://developers.facebook.com/docs/graph-api/reference/page/videos/#publish
https://developers.facebook.com/docs/reference/api/video/
From the facebook video API reference:
An individual Video in the Graph API.
To read a Video, issue an HTTP GET request to /VIDEO_ID with the
user_videos permission. This will return videos that the user has
uploaded or has been tagged in.
Video POST requests should use graph-video.facebook.com.
So you should be posting to graph-video.facebook.com if you are to upload video.
You also need extended permissions from the user or profile you'll be uploading to, in this case you need video_upload this is going to be requested once only, when the user currently logged in is asked for such permission for the app.
And your endpoint should be:
https://graph-video.facebook.com/me/videos
If you always want to post to a specific user than you'll have to change the endpoint part from /me to the User ID or page ID.
Here's a sample (in PHP):
$app_id = "YOUR_APP_ID";
$app_secret = "YOUR_APP_SECRET";
$my_url = "YOUR_POST_LOGIN_URL";
$video_title = "YOUR_VIDEO_TITLE";
$video_desc = "YOUR_VIDEO_DESCRIPTION";
$code = $_REQUEST["code"];
if(empty($code)) {
$dialog_url = "http://www.facebook.com/dialog/oauth?client_id="
. $app_id . "&redirect_uri=" . urlencode($my_url)
. "&scope=publish_stream";
echo("<script>top.location.href='" . $dialog_url . "'</script>");
}
$token_url = "https://graph.facebook.com/oauth/access_token?client_id="
. $app_id . "&redirect_uri=" . urlencode($my_url)
. "&client_secret=" . $app_secret
. "&code=" . $code;
$access_token = file_get_contents($token_url);
$post_url = "https://graph-video.facebook.com/me/videos?"
. "title=" . $video_title. "&description=" . $video_desc
. "&". $access_token;
echo '<form enctype="multipart/form-data" action=" '.$post_url.' "
method="POST">';
echo 'Please choose a file:';
echo '<input name="file" type="file">';
echo '<input type="submit" value="Upload" />';
echo '</form>';
Although I'm concerned about the upload speed if the videos are too big, but I'm guessing your customer has already sorted that out (compress/optimize/short videos etc.)
I've made you a demo here. Go to my website (I own that domain) and try to upload a video. I tried with this one which is a relatively small 4Mb file. Be sure that this script will only try to upload a video, nothing more (to the FB profile you are currently logged in, that is) but, if you are still concerned, copy my snippet, upload it to your own server (with PHP support of course) and create a test app where the site url is that domain and be sure to specify in the $my_url variable your endpoint which is basically the full path to your script receiving responses from facebook:
http://yourdomain.com/testfb.php
If you still want to do it on a desktop app then you have to go to developer.facebook.com on your app settings:
Settings > Advanced
And look for the first option:
And enable that switch so that facebook allows you to POST from a desktop or native app instead of a web server.
Note: I'm not an expert on Ruby, but the above working PHP code should be pretty obvious and easy to port to it.
as far as I recall, what you want isn't really possible without some kind of endpoint that can receive a callback from facebook.
If you can finagle an oauth token, from say the Graph API Explorer, then it becomes pretty trivial to use a gem like koala to upload your video.
here's the salient bit:
#graph = Koala::Facebook::API.new(access_token)
#graph.put_video(path_to_my_video)
I've made you a sample project here: fb-upload-example

Generating Paypal Signature, 'X-PAYPAL-AUTHORIZATION' in Ruby

Is there any library in Ruby that generates the Signature, 'X-PAYPAL-AUTHORIZATION' header that is required to make calls on behalf of the account holder who has authorized us through the paypal Permissions API.
I am done with the permissions flow and get the required access token, tokenSecret. I feel I am generating the signature incorrectly as all my calls with the the generated 'X-PAYPAL-AUTHORIZATION' fail. They give the following errors:
For NVP call I get:
You do not have permissions to make this API call
And for the GetBasicPersonalData call I get:
Authentication failed. API credentials are incorrect.
Has anyone gone through this in Ruby? What is best way to generate signature. Paypal has just provided some SDK in Paypal, Java, but not the algorithm to generate signature.
Thanks,
Nilesh
Take a look at the PayPal Permissions gem.
https://github.com/moshbit/paypal_permissions
Specifically lib/paypal_permissions/x_pp_authorization.rb
require 'cgi'
require 'openssl'
require 'base64'
class Hash
def to_paypal_permissions_query
collect do |key, value|
"#{key}=#{value}"
end.sort * '&'
end
end
module ActiveMerchant #:nodoc:
module Billing #:nodoc:
module XPPAuthorization
public
def x_pp_authorization_header url, api_user_id, api_password, access_token, access_token_verifier
timestamp = Time.now.to_i.to_s
signature = x_pp_authorization_signature url, api_user_id, api_password, timestamp, access_token, access_token_verifier
{ 'X-PAYPAL-AUTHORIZATION' => "token=#{access_token},signature=#{signature},timestamp=#{timestamp}" }
end
public
def x_pp_authorization_signature url, api_user_id, api_password, timestamp, access_token, access_token_verifier
# no query params, but if there were, this is where they'd go
query_params = {}
key = [
paypal_encode(api_password),
paypal_encode(access_token_verifier),
].join("&")
params = query_params.dup.merge({
"oauth_consumer_key" => api_user_id,
"oauth_version" => "1.0",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_token" => access_token,
"oauth_timestamp" => timestamp,
})
sorted_query_string = params.to_paypal_permissions_query
base = [
"POST",
paypal_encode(url),
paypal_encode(sorted_query_string)
].join("&")
base = base.gsub /%([0-9A-F])([0-9A-F])/ do
"%#{$1.downcase}#{$2.downcase}" # hack to match PayPal Java SDK bit for bit
end
digest = OpenSSL::HMAC.digest('sha1', key, base)
Base64.encode64(digest).chomp
end
# The PayPalURLEncoder java class percent encodes everything other than 'a-zA-Z0-9 _'.
# Then it converts ' ' to '+'.
# Ruby's CGI.encode takes care of the ' ' and '*' to satisfy PayPal
# (but beware, URI.encode percent encodes spaces, and does nothing with '*').
# Finally, CGI.encode does not encode '.-', which we need to do here.
def paypal_encode str
s = str.dup
CGI.escape(s).gsub('.', '%2E').gsub('-', '%2D')
end
end
end
end
Sample parameters:
url = 'https://svcs.sandbox.paypal.com/Permissions/GetBasicPersonalData'
api_user_id = 'caller_1234567890_biz_api1.yourdomain.com'
api_password = '1234567890'
access_token = 'YJGjMOmTUqVPlKOd1234567890-jdQV3eWCOLuCQOyDK1234567890'
access_token_verifier = 'PgUjnwsMhuuUuZlPU1234567890'
The X-PAYPAL-AUTHORIZATION header [is] generated with URL "https://svcs.paypal.com/Permissions/GetBasicPersonalData". (see page 23, and chapter 7, at the link)
NVP stating "You do not have permissions to make this API call" means your API credentials are correct, just that your account does not have permission for the particular API you are trying to call. Something between the two calls you are submitting is not using the same API credentials.
For NVP call I get:
What NVP call?
TransactionSearch (see comments below)
Also, if you haven't already done so, you will want to use the sandbox APP-ID for testing in the sandbox, and you will need to apply for an app-id with Developer Technical Services (DTS) at PayPal to get an App-ID for live.
EDIT:
To use the TransactionSearch API, all you should be submitting is below. You do not need to specify any extra headers.
USER=xxxxxxxxxxxxxxxxxx
PWD=xxxxxxxxxxxxxxxxxx
SIGNATURE=xxxxxxxxxxxxxxxxxx
METHOD=TransactionSearch
VERSION=86.0
STARTDATE=2009-10-11T00:00:00Z
TRANSACTIONID=1234567890
//And for submitting API calls on bob's behalf, if his PayPal email was bob#bob.com:
SUBJECT=bob#bob.com

Resources