OmniAuth Wave OAuth2 Strategy
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 installWave Developer Setup
- Sign up at Wave and create a business
- Go to the Wave Developer Portal
- Create a new application
- Note your Client ID and Client Secret
- 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']
endWith 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
endAuth 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
uidis the Wave Business ID (Base64-encoded format likeQnVzaW5lc3M6...). Falls back to User ID if no business exists. -
Scopes: Requests
account:read business:read user:readby 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}"
endCheck 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 rubocopContributing
Bug reports and pull requests are welcome on GitHub at https://github.com/dan1d/omniauth-wave-oauth2.
- Fork it
- Create your feature branch (
git checkout -b feature/my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin feature/my-new-feature) - Create a new Pull Request
License
The gem is available as open source under the terms of the MIT License.