Closed pacMakaveli closed 3 years ago
It looks like you are not posting the body as a json string. Can you convert the body to json first and try to send that to the endpoint?
Sure:
body = {
'RelyingParty' => 'http://auth.xboxlive.com',
'TokenType' => 'JWT',
'Properties' => {
'AuthMethod' => 'RPS',
'SiteName' => 'user.auth.xboxlive.com',
'RpsTicket' => "d=#{ access_token }"
}
}.to_json
Updated code and update response:
https://login.live.com/oauth20_authorize.srf?client_id=''&response_type=code&approval_prompt=auto&scope=Xboxlive.signin+Xboxlive.offline_access&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fmy_auth%2Fxbox
Please input your code:
M.R3_BAY.14a0e063-d5c2-0e0b-ba5d-2312b9fdc4ef
opening connection to user.auth.xboxlive.com:443...
opened
starting SSL for user.auth.xboxlive.com:443...
SSL established, protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384
<- "POST /user/authenticate HTTP/1.1\r\nX-Xbl-Contract-Version: 1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: user.auth.xboxlive.com\r\nContent-Length: 1236\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"
<- "{\"RelyingParty\":\"http://auth.xboxlive.com\",\"TokenType\":\"JWT\",\"Properties\":{\"AuthMethod\":\"RPS\",\"SiteName\":\"user.auth.xboxlive.com\",\"RpsTicket\":\"d=EwAwA+pvBAAUKods63Ys1fGlwiccIFJ+qE1hANsAAUEjM0aHApvcV+Wm4Z2Oax8ux3G0HcYZw9tBjtKYpMsikcv4Rs9GF44gjRrrQQPQ6y+eiF+9iWJbNRbzMo5vGbjY2dO/QL5TXexrEGu/94mUnT2F4k7PNcUl7clEnc0QfE4ANDOjxefmdRFA6ECe9aGalv4mLeDeJEc07xzjm/oV8ROIcYXZz4AJRFzu57kp2FcmfuE2TGxbds32Lq0SsbeTeNP5GNLg+9rsHyf92+BVva3Ylkmx3t5IVfoQ9O1Ila4hwHdGT4NvSs7/gPQcUw94zj2dGsj4ONeZXpJUSJe7x15It5ox9/ls8/BFcO3AwM/BSYWe4ZwEcq8GrSfpvKYDZgAACLZPXT1cFMMZAAK901k7ZxOUMteDJt7jw7TREWnBFEJuPjDADhnD76ifFkBPldEnYwQFhqBv36gx8R4Ure8l71nELhud5/HVn6K1bU6+cwuaCrZTfQfz0kNhbvXc9f5yBxv3tEFnx00y76EwME8HO/ZDZWAUME9t4Y+qzqIodIjO5z7eIdXeCREgslbTrhvWMdO9PydR7X2zOewE8a9vdAiq/fTeJa/ZiSzETzui+ZfsS8yCUiKWE00oAEmBWj6ZsCdYGkjmKTbjTQLTXyamO4Qi3ANqitycPJ9fDk2s0UOSLDlZND07HPrrfVx3MOaOOaAyBrksghEXqIn5+3R+ewcv740a1s728aHpybFgg8h0KIFLgwtjnVeC935BvqmvlpmGYvIxxo73eWOz2KaUuZkf68URns74CT+vl2mDp3WMQxuETh05q+qzQR+bKNEuw6duYD089j/+Zb2maAJuWL3MKCIbvE2fKvKG0jFqjjEoJ9IBCOKwS+hSVsGhH7ZCdbBF92Ae4X3ecbfh1DgRvldyYAt1zGt/FOennS8AdOvEUNUNzkIvecOjtGw0JlIRfcSqlz4ZN0CuLQ5cdEVUA+dpt/uDvXJ2B0lFh8iQWTAIozAygo2G832wVEMN4xD5MOZUgbJsg4FsA3JXGdaRtUdrCzSvWzeXewvoBjx271iVZf9G1pRCL+A8eS0C\"}}"
-> "HTTP/1.1 400 Bad Request\r\n"
-> "Cache-Control: no-cache, no-store\r\n"
-> "Content-Length: 0\r\n"
-> "MS-CV: wqynAfCIDEqduFejnfmwYA.0\r\n"
-> "X-Content-Type-Options: nosniff\r\n"
-> "X-XblCorrelationId: 00000000-0000-0000-0000-000000000000\r\n"
-> "Date: Thu, 14 Jan 2021 13:13:05 GMT\r\n"
-> "Connection: close\r\n"
-> "\r\n"
reading 0 bytes...
-> ""
read 0 bytes
Conn close
If it helps, also:
I just ran your library and it seems to work with my client id and redirect_uri , so the problem must be on my end in my post request.
I'll start comparing the requests and see what's different. I'll close this as it's on my end but if I sort it, I will come back with my fix and hopefully save some time for others that are dealing with the same issue.
Thanks for getting back @unknownskl
I could've sworn that this was my first 'fix' I tried when I was getting this issue. Remember to send JSON, kids!
headers = {
'Content-Type' => 'application/json',
'x-xbl-contract-version' => '1'
}
@pacMakaveli did you ever finish the port to Ruby? I would be really interested as I also use Ruby/Rails for my main app and want to use this authentication setup!
Sure did! I'm not going to post the gem I'm currently using because of personal reasons, but this is the full relevant piece of code which should get you up and running.
require 'httparty'
module XBOX
module Oauth
class Client
include HTTParty
DEFAULT_SCOPES ||= ['XboxLive.signin', 'XboxLive.offline_access'].join(' ').freeze
USER_AGENT ||= [
'Mozilla/5.0 (games.directory/2.0; XboxLiveAuth/3.0)',
'AppleWebKit/537.36 (KHTML, like Gecko)',
'Chrome/71.0.3578.98 Safari/537.36'
].join(' ').freeze
CLIENTS ||= {
MY_XBOX_LIVE: '0000000048093EE3',
XBOX_APP: '000000004C12AE6F'
}.freeze
def self.authorization_url
url = self.new.oauth_authorize_url
if XBOX::Oauth.configuration.debug
puts "Follow this link '#{ url }' and input the code returned below."
puts 'Code: '
code = gets.rstrip
# Start the Authentication flow
#
self.new.authenticate(code)
end
url
end
def initialize
raise 'Please read the README.md on how to configure the XBOX::Oauth module.' unless XBOX::Oauth.valid?
end
def authenticate(code, refresh: false)
# Start the Authentication flow
#
access_token, refresh_token = request_oauth_token(code, refresh: refresh)
user_token = get_user_token(access_token)
session = get_xsts_token(user_token)
# Return a new Session containing all the required information to start communicating with XBOX Live API
#
XBOX::Oauth::Session.new(session, access_token, refresh_token)
end
def refresh(refresh_token)
authenticate(refresh_token, refresh: true)
end
# TODO: Add a way for the User to request the app be removed from their allowed oauth services
#
def delete(refresh_toke)
# self.class.delete()
end
### Authorize the account for the app in order to request an authorization code.
#
# Redirect the client to Microsoft' website where he will be prompted for login details.
# If the Azure app has been configured correctly, on successful login, Microsoft will redirect back to the app using
# the @redirect_uri configuration value along with a code which has to be passed to @oauth20_token
#
# @return Encoded URL to be used in redirecting the client
# @example https://login.live.com/oauth20_authorize.srf?client_id=client_id&response_type=code&approval_prompt=auto&scope=Xboxlive.signin+Xboxlive.offline_access&redirect_uri=redirect_uri
#
def oauth_authorize_url
params = {
client_id: XBOX::Oauth.configuration.client_id,
response_type: 'code',
approval_prompt: 'auto',
scope: DEFAULT_SCOPES,
redirect_uri: XBOX::Oauth.configuration.redirect_uri
}
"https://login.live.com/oauth20_authorize.srf?#{ URI.encode_www_form(params) }"
end
## Authenticate the account via the authorization code returned by @oauth_authorize_url and receive an access and
# refresh token
#
# @return [Hash] containing the access_token, refresh_token, expiration_date, etc..
# @example
#
def request_oauth_token(code, refresh: false)
raise '"code" is empty; you need it silly! Did you forget to launch @authorization_url?' unless code
body = {
grant_type: (refresh ? 'refresh_token' : 'authorization_code'),
client_id: XBOX::Oauth.configuration.client_id,
scope: DEFAULT_SCOPES,
redirect_uri: XBOX::Oauth.configuration.redirect_uri,
}
body[refresh ? 'refresh_token' : 'code'] = code
body[:client_secret] = client_secret unless (client_secret = XBOX::Oauth.configuration.client_secret)
request = self.class.post('https://login.live.com/oauth20_token.srf',
body: body
).parsed_response
raise request['error_description'] if request.has_key?('error')
return request['access_token'], request['refresh_token']
end
# Authenticate with XBOX Live using the user's access_token returned by @request_oauth_token and receive a
# user_token and uhs id which are to be used with @get_xsts_token
#
# @return
# @example
#
def get_user_token(access_token)
raise '"access_token" is empty; you need it silly!' unless access_token
request = self.class.post('https://user.auth.xboxlive.com/user/authenticate',
body: {
'RelyingParty' => 'http://auth.xboxlive.com',
'TokenType' => 'JWT',
'Properties' => {
'AuthMethod' => 'RPS',
'SiteName' => 'user.auth.xboxlive.com',
'RpsTicket' => "d=#{ access_token }"
}
}.to_json,
headers: {
'Content-Type' => 'application/json',
'x-xbl-contract-version' => '1'
}
)
request.parsed_response['Token']
end
# Authorize via user token and receive final X token
#
# @return [Hash]
#
def get_xsts_token(user_tokens = [])
raise '"user_tokens" is empty; you need it silly!' if user_tokens.empty?
request = self.class.post('https://xsts.auth.xboxlive.com/xsts/authorize',
body: {
'RelyingParty' => 'http://xboxlive.com',
'TokenType' => 'JWT',
'Properties' => {
'UserTokens' => [user_tokens],
# 'DeviceToken' => '',
# 'TitleToken' => '',
# 'OptionalDisplayClaims' => [''],
'SandboxId' => 'RETAIL'
}
}.to_json,
headers: {
'Content-Type' => 'application/json',
'x-xbl-contract-version' => '1'
}
)
raise 'An error occured in get_xsts_token' if request.code != 200
request.parsed_response
end
end
end
end
And this is how I use it in my app
# Placeholder for the XBOX Authentication service
# REVIEW: Create an omniauth-xbox gem wrapper for xbox-oauth and let omniauth deal with this
#
class MyAuthController < ApplicationController
def xbox
if (code = params[:code])
request = XBOX::Oauth::Client.new.authenticate(code)
user = User.find_for_oauth(request, 'xbox', current_user)
if user
sign_in(user, event: :authentication)
# Quick hack for allowing users to login with XBOX Live; No point in trying to figure
# out a nicer way since all this will be handled by omniauth.
#
redirect_to user_url(current_user), fallback_location: root_url
end
else
redirect_to XBOX::Oauth::Client.authorization_url
end
end
end
Hi, does this method still work? I seem to hit 400 Bad Request whenever I try to authenticate the user. I know the authenticate logic works because it works fine when a token is returned using the username/password flow, just not with oauth.
Is this related to?
Code if needed: It's ruby, but the flow is the same, just trying to port it for my ruby app.
I've already tried: https://gist.github.com/tuxuser/8b7cc153cdecd0a9c3f2694850fa90bd with no luck.
Here's the request: