AuthRocket/Ruby
The authrocket
gem works with both Rails and plain Ruby. It will auto-detect Rails and enable Rails-specific features as appropriate.
It covers all of our Core API and select portions of the Configuration API.
For our Getting Started guide, see Integration with Rails or Integration with Ruby.
Usage - Rails
AuthRocket includes a streamlined Rails integration that automatically handles logins and logouts. For a new app, we highly recommend this.
To your Gemfile, add:
gem 'authrocket', require: 'authrocket/rails'
Then ensure the following environment variable is set:
LOGINROCKET_URL = https://sample.e2.loginrocket.com/
If you’ve changed the default JWT key type to HS256, you’ll also need this variable:
AUTHROCKET_JWT_KEY = jsk_SAMPLE
If you plan to access the AuthRocket API as well, you’ll need these variables too:
AUTHROCKET_API_KEY = ks_SAMPLE
AUTHROCKET_URL = https://api-e2.authrocket.com/v2
AUTHROCKET_REALM = rl_SAMPLE # optional
Finally, add a before_action
command to any/all controllers or actions that should require a login.
For example, to protect your entire app:
class ApplicationController < ActionController::Base
before_action :require_login
end
Selectively exempt certain actions or controllers using the standard skip_before_action
method:
class ContactUsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
Helpers are provided to create login, signup, and logout links, as well as for users to manage their profile:
<%= link_to 'Login', ar_login_url %>
<%= link_to 'Signup', ar_signup_url %>
<%= link_to 'Logout', logout_path %>
<%= link_to 'Manage Profile', ar_profile_url %>
Both the current Session and User are available to your controllers and views:
current_session # => AuthRocket::Session
current_user # => AuthRocket::User
The current Membership and Org (account) are accessible through helpers as well.
current_membership
current_org
If a user is a member of more than one org (account), current_membership
and current_org
will be reflect the currently selected account. Additional helpers are available to provide appropriate links to your users:
<%= link_to 'Manage current account', ar_account_url %>
<%= link_to 'Switch accounts', ar_accounts_url %>
See below for customization details.
Usage - everywhere else
If you aren’t using Rails, or if the streamlined integration above is too opinionated, use the gem without the extra Rails integration.
In your Gemfile, add:
gem 'authrocket'
Then set the following environment variables:
# If accessing the AuthRocket API:
AUTHROCKET_API_KEY = ks_SAMPLE
AUTHROCKET_URL = https://api-e2.authrocket.com/v2 # must match your provisioned cluster
AUTHROCKET_REALM = rl_SAMPLE # optional
#
# If using JWT-verification of AuthRocket's login tokens:
AUTHROCKET_JWT_KEY = SAMPLE
Configuration
By default, AuthRocket automatically loads credentials from environment variables. This is optimal for any 12-factor deployment. Supported variables are:
AUTHROCKET_API_KEY = ks_SAMPLE
Your AuthRocket API key. Required to use the API (but not if only performing JWT verification of login tokens).
AUTHROCKET_JWT_KEY = SAMPLE
Used to perform JWT signing verification of login tokens. Not required if validating all tokens using the API instead. Also not required if LOGINROCKET_URL is set and RS256 keys are being used, as public keys will be auto-retrieved. This is a realm-specific value, so like AUTHROCKET_REALM
, set it on a per-use basis if using multiple realms.
AUTHROCKET_REALM = rl_SAMPLE
Sets an application-wide default realm ID. If you’re using a single realm, this is definitely easiest. Certain multi-tenant apps might use multiple realms. In this case, don’t set this globally, but include it as part of the :credentials
set for each API method.
AUTHROCKET_URL = https://api-e2.authrocket.com/v2
The URL of the AuthRocket API server. This may vary depending on which cluster your service is provisioned on.
LOGINROCKET_URL = https://SAMPLE.e2.loginrocket.com/
The LoginRocket URL for your Connected App. Used by the streamlined Rails integration (for redirects) and for auto-retrieval of RS256 JWT keys (if AUTHROCKET_JWT_KEY is not set). If your app uses multiple realms, you’ll need to handle this on your own. If you’re using a custom domain, this will be that domain and will not contain ‘loginrocket.com’.
It’s also possible to configure AuthRocket using a Rails initializer (or other initialization code).
AuthRocket::Api.credentials = {
api_key: 'ks_SAMPLE',
jwt_key: 'SAMPLE',
loginrocket_url: 'https://sample.e2.loginrocket.com/',
realm: 'rl_SAMPLE',
url: 'https://api-e2.authrocket.com/v2'
}
Customizing the Rails integration
The built-in Rails integration tries to handle as much for you as possible. However, there may be times when you wish to modify the default behavior.
Logins
The Rails integration handles logins on any path by detecting the presence of ?token=...
. It will process the login and then immediately redirect back to the same path without ?token=
. This helps prevent browsers and bookmarks from accidentally saving or caching the login token.
Likewise, the built-in handler for before_action :require_login
will automatically redirect to LoginRocket when the user is not currently logged in. ?redirect_uri=<current_path>
will be automatically included so that the user returns to the same place post-login. You can override this behavior by replacing before_login
.
# For example, to force the user to always return to "/manage":
def require_login
unless current_session
redirect_to ar_login_url(redirect_uri: "/manage"), allow_other_host: true
end
end
AuthRocket will verify the domain + path to redirect to. You can configure this at Realm -> Settings -> Connected Apps -> (edit) -> Login URLs. The first URL listed will be the default, so it should generally match your “just logged in” path.
Paths are validated as “equal or more specific”. That is, if Login URLs contains https://my.app/manage
, then any path starting with /manage
will be allowed, but /other
will not be allowed. If you want to allow any path at your domain, add https://my.app/
(since /
will match any path).
Logouts
The default post-logout path
Upon logout, the user will be returned to the root path (“/”).
This default path may be changed using an initializer. Create/edit config/initializers/authrocket.rb
and add:
AuthRocket::Api.post_logout_path = '/other'
/logout route
The default route for logout is /logout
. To override it, add an initializer for AuthRocket (eg: config/initializers/authrocket.rb
) and add:
AuthRocket::Api.use_default_routes = false
Then add your own route to config/routes.rb
:
get 'mylogout' => 'logins#logout'
The logout action
AuthRocket’s default login controller automatically sets a logout message using flash
.
You may customize this, or other logout behavior, by creating your own LoginsController and inheriting from AuthRocket’s controller:
class LoginsController < AuthRocket::ArController
def logout
super
flash[:notice] = 'You have been logged out.'
end
end
If you wish to replace all of the login logic, create a new controller that doesn’t inherit from AuthRocket::ArController
(and also override the routes, as per above). You may wish to look at ArController
as a reference.
Verifying login tokens
If you’re not using the streamlined Rails integration, you’ll need to verify login tokens on your own (unless you’re using the API to authenticate directly).
JWT verification
AuthRocket’s login tokens use the JWT standard and are cryptographically signed. Verifying the signature is extremely fast. Here’s an example:
def current_user
@_current_user ||= AuthRocket::Session.from_token(session[:ar_token])&.user
end
from_token
returns nil
if the token is missing, expired, or otherwise invalid.
API verification
AuthRocket also supports Managed Sessions, which enables you to enforce logouts, even across apps (single sign-out!). In this instance, the session is regularly verified using the AuthRocket API.
def current_user
@_current_user ||= AuthRocket::Session.retrieve(session[:ar_token], cache: { expires_in: 15.minutes })&.user
end
For better performance (and to avoid API rate limits), you will want to cache the results of the API call for 3-15 minutes.
If using Rails, Rails.cache
is used by default. Otherwise, you must configure a cache store for AuthRocket. In either case, see Caching below.
Initial login
Each of the above are designed for ongoing use. The initial login isn’t going to be much different though. Here’s an example login action:
def login
if params[:token]
if AuthRocket::Session.from_token(params[:token])
session[:ar_token] = params[:token]
redirect_to '/'
return
end
end
redirect_to AuthRocket::Api.credentials[:loginrocket_url], allow_other_host: true
end
Changing locales
The AuthRocket Core API supports multi-locale access. See Localization for the currently supported locales and other details.
If you are using the streamlined Rails integration alongside LoginRocket, it may not be necessary to set the locale for API access. The locale is primarily used for generating localized error messages. This is only useful for API operations that might generate errors. When handling logins and signups via LoginRocket, LoginRocket will handle all of this for you.
When the Accept-Language
header is not sent, the API defaults to English.
Global locale
To set a global locale for your app, add this to your AuthRocket initializer:
AuthRocket::Api.default_headers.merge!(
accept_language: 'en'
)
Per-request locale
If your app supports multiple locales, then you’ll likely want to set the locale on a per-request basis. Add a headers: { accept_language: 'en' }
argument to relevant API calls:
AuthRocket::User.create(
email: 'jdoe@example.com',
password: 'secret!',
headers: { accept_language: 'en' }
)
Caching
The AuthRocket gem is capable of caching the results of GET requests. Since authentication and user data generally needs to be timely, this is opt-in on a per-request basis. The most common use is when validating sessions via the API.
To enable caching, a cache store must be configured. On Rails, authrocket
automatically uses Rails.cache, so simply ensure that’s setup appropriately.
If not using Rails (or if you wish to use a different cache store even when using Rails), add this to your AuthRocket initializer:
cache_options = {} # app specific
AuthRocket::Api.cache_store = RedisCacheStore.new(cache_options)
Any Rails-compatible cache store should work.
Next, enable the cache for specific API calls:
# To avoid caching for too long, it's recommended to set a specific expiration time.
AuthRocket::Session.retrieve(token, cache: { expires_in: 5.minutes })
# However, it's possible to leave out :expires_in and use the cache store's default.
# Warning: Ensure the cache store has a default expiration, otherwise cache entries
# will last forever!
AuthRocket::Session.retrieve(token, cache: {}) # These are identical
AuthRocket::Session.retrieve(token, cache: true)
# All options in cache: { ... } are passed directly to the cache store, so anything
# supported by your cache store is valid.
AuthRocket::Session.retrieve(token, cache: { expires_in: 15.minutes, force: true })
Requests
See the AuthRocket API documentation for available APIs, parameters, and responses, all with Ruby-specific examples.
Overview of common requests
Most resources support some or all of the below. Check specific APIs to confirm existence of specific functions.
List [resources]
For any resource with a “List [resources]” method, both all()
and first()
are available.
# Retrieve multiple resources at once
results = AuthRocket::Org.all
results[0].id # ID of the first returned resource
# Retrieve the first 20 resources
results = AuthRocket::Org.all(max_results: 20)
# Retrieve the next 20 resources
if results.more_results?
results = AuthRocket::Org.all(
max_results: 20,
after: results.last.id # ID of the last resource previously received
)
end
# A convenience function to retrieve the first resource when the ID isn't known.
# If there are no matching resources, returns null.
result = AuthRocket::Org.first
result = AuthRocket::Org.first(
state: 'active'
)
result.id # ID of the returned resource
Get a [resource]
For any resource with a “Get a [resource]” method, both find()
and retrieve()
are available.
result = AuthRocket::Org.find('org_SAMPLE')
result.id # ID of the returned resource
result.name # Name field of the returned resource
# raises an AuthRocket::RecordNotFound exception if ID not found
result = AuthRocket::Org.find('org_SAMPLE',
some: 'condition',
...
)
# retrieve() is identical to find() except that it returns nil if ID not found
if result = AuthRocket::Org.retrieve('org_SAMPLE')
...
else
# not found
end
Create a [resource]
For any resource with a “Create a [resource]” method, both create()
and create!()
are available.
result = AuthRocket::Org.create(
name: 'Widgets Inc'
)
if result.errors?
result.errors # array of error messages
end
if result.valid? # opposite of result.errors?
result.id # ID of the new resource
end
# Like ActiveRecord, create!() raises an AuthRocket::RecordInvalid
# exception if the new resource cannot be created.
begin
result = AuthRocket::Org.create!(
name: 'Widgets Inc'
)
rescue AuthRocket::RecordInvalid => e
e.object # contains the invalid resource
e.object.errors # array of error messages
end
Update a [resource]
For any resource with an “Update a [resource]” method, .update()
, .update!()
, #update()
, and #update!()
are all available.
# Update a resource without finding it first. Returns updated resource.
result = AuthRocket::Org.update('org_SAMPLE',
name: 'Widgets Inc'
)
result.errors? # like create() above
result.valid? # "
result.errors # "
# Like update(), but raises an AuthRocket::RecordInvalid exception if
# the new resource cannot be updated. See create!() above.
result = AuthRocket::Org.update!('org_SAMPLE',
name: 'Widgets Inc'
)
# If the resource is already loaded, it can be updated directly too.
# In this case, #update() returns false if there was an error.
# #update!() raises an exception as expected.
resource = AuthRocket::Org.find('org_SAMPLE')
if resource.update(name: 'Widgets Inc')
# success
else
resource.errors # array of error messages
end
resource.update!(name: 'Widgets Inc')
Delete a [resource]
For any resource with a “Delete a [resource]” method, .delete()
, .delete!()
, #delete()
, and #delete!()
are all available.
# Delete a resource without finding it first.
# In most cases, returns a pseudo-resource (with just the ID field) on
# success. On occasion the API returns the entire resource.
# In either case, #errors?, #valid?, and #errors are all available,
# just like with #update.
result = AuthRocket::Session.delete('kss_SAMPLE')
result = AuthRocket::Org.delete('org_SAMPLE', force: true)
result.errors? # like create() above
result.valid? # "
result.errors # "
# Like delete(), but raises an AuthRocket::RecordInvalid exception if
# the new resource cannot be deleted/updated, likely due to some
# dependency or state validation requirement. See create!() above.
result = AuthRocket::Org.delete!('org_SAMPLE')
# If the resource is already loaded, it can be deleted directly too.
# In this case, #delete() returns false if there was a validation error.
resource = AuthRocket::Org.find('org_SAMPLE')
if resource.delete
# success
else
resource.errors # array of error messages
end
resource.delete!
# Hint: in all cases, if the resource is already deleted, an
# AuthRocket::RecordNotFound exception will be raised.
Per-request credentials, caching, and headers
Credentials may be modified on a per-request basis. If :url
is not provided, the global :url
value is used. No other global values are inherited. Allowed values are the same as when setting AuthRocket::Api.credentials = { ... }
. See Configuration above.
resource = AuthRocket::Org.find('org_SAMPLE',
credentials: { ... }
)
# Credentials are remembered when updating or deleting an already
# retrieved resource.
resource.update(...) # Uses same :credentials as above
Caching may also be enabled per-request. See Caching above for details.
resource = AuthRocket::Org.find('org_SAMPLE',
cache: { ... }
)
Likewise, headers may be added or modified. Headers are merged with the default headers, so only new ones need to be provided.
AuthRocket::Org.create(
name: 'Widgets Inc'
headers: { accept_language: 'en' }
)
On the off chance that a resource field name collides with :credentials
, :cache
, or :headers
, resource fields may be placed inside :params
. Using :params
is allowed at any time, and is occasionally handy for other purposes too.
It’s also worth noting that symbol and string keys are both allowed, and may be mixed and matched as long as they don’t duplicate one another. There’s no need to convert them ahead of time.
# These are the same, but the latter assures that field names cannot
# conflict with other arguments, like :credentials.
AuthRocket::User.create(
first_name: 'John',
'last_name' => 'Doe',
credentials: { ... }
)
AuthRocket::User.create(
params: {
first_name: 'John',
'last_name' => 'Doe',
},
credentials: { ... }
)
# Mixing inside and outside :params is not allowed.
AuthRocket::User.create(
first_name: 'John', # will be ignored because :params is present
params: { last_name: 'Doe' } # will be used
)
Responses
API methods that return multiple results will return an Array with an added more_results?
method.
results = AuthRocket::Org.all(max_results: 20)
results.each{|resource| ... }
if results.more_results?
# retrieve next set of results, show a Next button, etc.
end
Fields may be accessed directly or with Hash syntax.
# These are all the same.
resource.id
resource[:id]
resource['id']
Rails compatibility
Resource objects are quite ActiveModel-like and generally compatible with ActiveView, including Rails form helpers.
One difference is attributes cannot be changed in advance of persistence. Instead, call update(...)
directly with new values.
# NOT allowed
resource.name = 'New name'
resource.save
# Do this instead
resource.update(name: 'New name')
While objects cannot be modified in advance, it can still be useful to build a new, empty object for the purposes of rendering a form.
def new
@resource = AuthRocket::Org.new
end
<%= form_for @resource do |f| %>
<%= f.text_field :name %>
...
<% end %>