Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AuthorizationUnsuccessful error #556

Closed
itsaphel opened this issue Jan 12, 2022 · 2 comments
Closed

AuthorizationUnsuccessful error #556

itsaphel opened this issue Jan 12, 2022 · 2 comments

Comments

@itsaphel
Copy link

Steps:

  1. Request authorization_url and be redirected to it
  2. Xero shows a page to authorise access.
  3. Redirected back to my app with a code
  4. Use the code to get an access_token etc.
  5. Try to make a request using this information. Just get the error "AuthorizationUnsuccessful" with no other information.

Code

Setting up token:

  def xero_callback
    client = get_xero_client
    token = client.authorize_from_code(
      params[:code],
      redirect_uri: admin_root_url + "/xero_callback"
    )
    token = token.to_hash

    current_connections = client.current_connections
    tenant_id = current_connections[0].attributes['tenantId']
    token[:tenant_id] = tenant_id

    Rails.cache.write("xero_token", token)
  end

Making the request:

client = get_xero_client
conn = client.Contact.find("a")
puts conn

  def get_xero_client
    cached_creds = Rails.cache.read('xero_token')

    Xeroizer::OAuth2Application.new(
      Rails.configuration.global['xero']['client_id'],
      Rails.configuration.global['xero']['client_secret'],
      access_token: cached_creds[:access_token],
      refresh_token: cached_creds[:refresh_token],
      tenant_id: cached_creds[:tenant_id]
    )
  end

Exception:

Xeroizer::OAuth::TokenInvalid in Admin::CustomersController#index
AuthorizationUnsuccessful

(on the conn = client.Contact.find("a") line)

I've checked using a debugger and made sure that the access_token, refresh_token and tenant_id are all not null and appear to be proper strings.

@matthewbloch
Copy link

I know the example in the README.md has a typo and isn't complete, so it's not surprising that you're having a problem with making a minimal example.

However your code is also not complete :) If you're still stuck can you maybe paste an entire controller here that doesn't work, as the devil will be in the detail e.g. you might have a bug in how you're reloading the session between requests.

Here's the complete, minimal "works for me" example with Sinatra which I've not yet had a chance to contribute to the repo:

require 'active_support/isolated_execution_state' # https://github.com/waynerobinson/xeroizer/issues/555
require 'sinatra'
require 'xeroizer'

# Get these parameters at https://developer.xero.com/app/manage/
#
XERO_CLIENT_ID = "xxx"
XERO_SECRET = "xxx"

# https://developer.xero.com/documentation/oauth2/scopes
# N.B. must have offline_access for token refresh to work
XERO_SCOPE = %w(
    accounting.settings.read
    accounting.transactions
    accounting.contacts.read
    offline_access
).
join(" ")

# Make sure this matches the "Redirect URI" - can be localhost for testing!
#
XERO_REDIRECT="http://localhost:3000/redirect"

# Generate this with e.g. openssl rand -base64 30
SESSION_SECRET="xxx"

configure do
    enable :sessions
    set :session_secret, SESSION_SECRET
end

helpers do
    def save_xero_to_session
        session[:xero][:access_token] = @xero.access_token.token
        session[:xero][:refresh_token] = @xero.access_token.refresh_token
        session[:xero][:expires_at] = @xero.access_token.expires_at
        session[:xero][:tenant_id] = @xero.current_connections.first.tenant_id
    end

    def xero
        @xero = Xeroizer::OAuth2Application.new(
            XERO_CLIENT_ID,
            XERO_SECRET,
            session[:xero] ? {
                tenant_id: session[:xero][:tenant_id],
                access_token: session[:xero][:access_token],
                refresh_token: session[:xero][:refresh_token]
            } : {})
        if session[:xero] && Time.now+60 > Time.at(session[:xero][:expires_at])
            @xero.renew_access_token
            save_xero_to_session
        end
        @xero
    end
end

get '/login' do
    # Redirect user to Xero login page
    redirect xero.authorize_url(redirect_uri: XERO_REDIRECT, scope: XERO_SCOPE)
end

get '/redirect' do
    # Xero sends user back here once authenticated
    xero.authorize_from_code(params[:code], redirect_uri: XERO_REDIRECT)
    save_xero_to_session
    redirect '/invoices'
end

get '/invoices' do
    [200, {"content-type" => "text/plain"}, xero.Invoice.all(order: 'Date').inspect]
end

@itsaphel
Copy link
Author

Thanks @matthewbloch. It's a huge 😳 moment but, reading your example above and testing in curl (which worked, but I was trying a different endpoint), I realised my issue was the OAuth scopes I was requesting. I think my authorisation URL code originally looked something like:

  def get_xero_auth_url
    get_xero_client.authorize_url(
      redirect_uri: admin_root_url + "/xero_callback",
      scope: "accounting.settings.read offline_access"
    )
  end

which of course isn't the most useful of scopes, and so the requests for any meaningful data were being rejected. Changed that and all seems good now :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants