No release in over 3 years
An OmniAuth strategy for authenticating with Wave accounting (by H&R Block) using OAuth 2.0. Fetches user and business info via Wave's GraphQL API.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 2.0
~> 2.1
~> 13.0
~> 3.12
~> 1.75
~> 0.22
~> 3.18

Runtime

>= 1.0, < 3.0
 Project Readme

OmniAuth Wave OAuth2 Strategy

Gem Version

An OmniAuth strategy for authenticating with Wave (by H&R Block) using OAuth 2.0.

Installation

Add this line to your application's Gemfile:

gem 'omniauth-wave-oauth2'

Then execute:

$ bundle install

Wave Developer Setup

  1. Sign up at Wave and create a business
  2. Go to the Wave Developer Portal
  3. Create a new application
  4. Note your Client ID and Client Secret
  5. Add your Redirect URI (e.g., https://yourapp.com/auth/wave_oauth2/callback)

Note: Wave requires a Pro Plan ($19/mo) for third-party API access.

Usage

Standalone OmniAuth

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :wave_oauth2, ENV['WAVE_CLIENT_ID'], ENV['WAVE_CLIENT_SECRET']
end

With Devise

In config/initializers/devise.rb:

config.omniauth :wave_oauth2, ENV['WAVE_CLIENT_ID'], ENV['WAVE_CLIENT_SECRET']

Add to your routes:

devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }

Create the callbacks controller:

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def wave_oauth2
    @user = User.from_omniauth(request.env['omniauth.auth'])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication
    else
      redirect_to new_user_registration_url
    end
  end
end

Auth Hash

Here's an example of the authentication hash available in request.env['omniauth.auth']:

{
  "provider" => "wave_oauth2",
  "uid" => "QnVzaW5lc3M6abc123def456",
  "info" => {
    "email" => "owner@example.com",
    "name" => "Jane Doe",
    "first_name" => "Jane",
    "last_name" => "Doe",
    "business_name" => "Jane's Bakery"
  },
  "credentials" => {
    "token" => "ACCESS_TOKEN",
    "refresh_token" => "REFRESH_TOKEN",
    "expires_at" => 1704067200,
    "expires" => true
  },
  "extra" => {
    "raw_info" => {
      "user_id" => "user-abc",
      "email" => "owner@example.com",
      "first_name" => "Jane",
      "last_name" => "Doe",
      "name" => "Jane Doe",
      "business_id" => "QnVzaW5lc3M6abc123def456",
      "business_name" => "Jane's Bakery"
    }
  }
}

Wave OAuth2 Specifics

  • GraphQL API: Wave uses a GraphQL API at gql.waveapps.com/graphql/public (not REST). User and business info are fetched via GraphQL after token exchange.
  • Auth Scheme: Wave requires credentials in the POST body (auth_scheme: :request_body), not HTTP Basic Auth.
  • Token Expiry: Access tokens expire after ~2 hours. Refresh tokens are long-lived.
  • UID: The uid is the Wave Business ID (Base64-encoded format like QnVzaW5lc3M6...). Falls back to User ID if no business exists.
  • Scopes: Requests account:read business:read user:read by default.

Token Refresh

Wave access tokens expire after ~2 hours. This gem includes a TokenClient for refreshing tokens:

client = OmniAuth::WaveOauth2::TokenClient.new(
  client_id: ENV['WAVE_CLIENT_ID'],
  client_secret: ENV['WAVE_CLIENT_SECRET']
)

result = client.refresh_token(account.refresh_token)

if result.success?
  account.update!(
    access_token: result.access_token,
    refresh_token: result.refresh_token,
    token_expires_at: Time.at(result.expires_at)
  )
else
  Rails.logger.error "Token refresh failed: #{result.error}"
end

Check Token Expiration

# Check if token is expired (with 5-minute buffer by default)
client.token_expired?(account.token_expires_at)

# Custom buffer (e.g., refresh 10 minutes before expiry)
client.token_expired?(account.token_expires_at, buffer_seconds: 600)

TokenResult Object

Method Description
success? Returns true if refresh succeeded
failure? Returns true if refresh failed
access_token The new access token
refresh_token The new refresh token
expires_at Unix timestamp when token expires
expires_in Seconds until token expires
error Error message if failed
raw_response Full response hash from Wave

Development

bundle install
bundle exec rspec
bundle exec rubocop

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dan1d/omniauth-wave-oauth2.

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create a new Pull Request

License

The gem is available as open source under the terms of the MIT License.