Retrieves and refreshes OpenID tokens on behalf of a user when dealing with complex authentication schemes, such as client-side certificates.
Supported Ruby versions: 2.0.0 or higher
Licensed under the MIT license, see LICENSE for more information.
When using OpenID in native applications, the most common approach is to open the identity provider's authorization page in a web view, let the user authenticate and have the application hold on to access, identity and refresh tokens.
However, the above flow may be unusable if the identity provider provides complex authentication schemes, such as client-side certificates.
On iOS, client-side certificates stored in the system keychain cannot be obtained due to application sandboxing.
On Android, one can obtain system certificates but these can not be used within a web view.
When using OpenID token proxy, the application opens a web browser - which has access to client-side certificates regardless of storage location - and lets the user authenticate. The identity provider redirects to the OpenID token proxy, which in turn passes along any obtained tokens to the application.
Add this line to your application's Gemfile:
gem 'openid-token-proxy'
Or install it yourself:
$ gem install openid-token-proxy
OpenIDTokenProxy.configure do |config|
config.client_id = 'xxx'
config.client_secret = 'xxx'
config.issuer = 'https://login.windows.net/common'
config.redirect_uri = 'https://example.com/auth/callback'
config.resource = 'https://graph.windows.net'
# By default, only tokens issued for the resource above are accepted
# Alternatively, you can override the allowed audiences or allow multiple:
config.allowed_audiences = [
'https://id.hyper.no',
'https://graph.windows.net'
]
# Indicates which domain users will presumably be signing in with
config.domain_hint = 'example.com'
# Whether to force authentication in case a session is already established
config.prompt = 'login'
# If these endpoints or public keys are not configured explicitly, they will be
# discovered automatically by contacting the issuer (see above)
config.authorization_endpoint = 'https://login.windows.net/common/oauth2/authorize'
config.token_endpoint = 'https://login.windows.net/common/oauth2/token'
config.userinfo_endpoint = 'https://login.windows.net/common/openid/userinfo'
config.public_keys = [
OpenSSL::PKey::RSA.new("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9...")
]
# Alternatively, you can override the authorization URI in its entirety:
config.authorization_uri = 'https://id.hyper.no/authorize?prompt=login'
end
Alternatively, these environment variables will be picked up automatically:
OPENID_ALLOWED_AUDIENCES
(comma-separated, defaults toOPENID_RESOURCE
)OPENID_AUTHORIZATION_ENDPOINT
OPENID_AUTHORIZATION_URI
OPENID_CLIENT_ID
OPENID_CLIENT_SECRET
OPENID_DOMAIN_HINT
OPENID_ISSUER
OPENID_PROMPT
OPENID_REDIRECT_URI
OPENID_RESOURCE
OPENID_TOKEN_ENDPOINT
OPENID_USERINFO_ENDPOINT
OpenID token proxy's main task is to obtain tokens on behalf of users. To allow it to do so, start by mounting the engine in your Rails application:
Rails.application.routes.draw do
mount OpenIDTokenProxy::Engine, at: '/auth'
end
Next, register the engine's callback - https://example.com/auth/callback
- as
the redirect URL of your OpenID application on the issuer so that any authorization
requests are routed back to your application.
The proxy itself also needs to be configured with a redirect URL in order for it to know what to do with any newly obtained tokens. To boot back into a native applicaton one could use custom URL schemes or intents:
OpenIDTokenProxy.configure do |config|
config.token_acquirement_hook = proc { |token|
"my-app://?token=#{token}&refresh_token=#{token.refresh_token}"
}
end
Warning: Redirecting to any path with query parameters (e.g. example.com/?token=xxx
) could theoretically leak tokens to third parties through the Referer
-header for external assets.
Additionally, OpenID token proxy ships with an authentication module simplifying token validation for use in APIs:
class AccountsController < ApplicationController
include OpenIDTokenProxy::Token::Authentication
require_valid_token
...
end
Access tokens may be provided with one of the following:
X-Token
header.Authorization: Bearer <token>
header.- Query string parameter
token
. - Cookie
token
.
Token expiry time will be exposed through the X-Token-Expiry-Time
header.
A valid token is exposed to a controller as current_token
and identity information
can be extracted by providing a claim name through hash-syntax:
current_token['email']
Identity providers may support additional claims beyond the standard OpenID ones.
Most identity providers issue access tokens with short lifespans. To prevent users from having to authenticate often, refresh tokens are used to obtain new access tokens without user intervention.
OpenID token proxy's token refresh module does just that:
class AccountsController < ApplicationController
include OpenIDTokenProxy::Token::Authentication
include OpenIDTokenProxy::Token::Refresh
require_valid_token
...
end
Refresh tokens may be provided with one of the following:
X-Refresh-Token
header.- Query string parameter
refresh_token
. - Cookie
refresh_token
.
Whenever an access token has expired and a refresh token is given, the module will attempt to obtain a new token transparently.
The following headers will be present on the API response if, and only if, a new token was obtained:
X-Token
header containing the new access token to be used in future requests.X-Refresh-Token
header containing the new refresh token.
You may configure some code to be run (scoped to a controller) when a token is successfully refreshed:
OpenIDTokenProxy.configure do |config|
config.token_refreshment_hook = proc { |token|
cookies[:token] = token.access_token
}
end
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a pull request
Hyper made this. We're a digital communications agency with a passion for good code, and if you're using this library we probably want to hire you.