Project

safire

0.0
No release in over 3 years
A Ruby gem implementing SMART on FHIR and UDAP protocols for clients.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

Safire

Gem Version CI Coverage Documentation

Safire is a lean Ruby library that implements SMART on FHIR and UDAP client protocols for healthcare applications.


Features

SMART on FHIR App Launch (v2.2.0)

  • Discovery (/.well-known/smart-configuration)
  • Public Client (PKCE)
  • Confidential Symmetric Client (client_secret + HTTP Basic Auth)
  • Confidential Asymmetric Client (private_key_jwt with RS384/ES384)
  • POST-Based Authorization

UDAP

Planned. See ROADMAP.md for details.


Installation

Requires Ruby ≥ 4.0.2.

gem 'safire'
bundle install

Quick Start

require 'safire'

# Step 1 — Create a client (Hash config or Safire::ClientConfig.new)
client = Safire::Client.new(
  base_url:     'https://launch.smarthealthit.org/v/r4/sim/eyJoIjoiMSJ9/fhir',
  client_id:    'my_client_id',
  redirect_uri: 'https://myapp.example.com/callback',
  scopes:       ['openid', 'profile', 'patient/*.read']
)

# Step 2 — Discover SMART metadata (lazy — only called when needed)
metadata = client.server_metadata
puts metadata.authorization_endpoint
puts metadata.capabilities.join(', ')

# Step 3 — Build the authorization URL (Safire generates state + PKCE automatically)
auth_data = client.authorization_url
# auth_data => { auth_url:, state:, code_verifier: }
# Store state and code_verifier server-side, redirect the user to auth_data[:auth_url]

# Step 4 — Exchange the authorization code for tokens (on callback)
token_data = client.request_access_token(
  code:          params[:code],
  code_verifier: session[:code_verifier]
)
# token_data => { "access_token" => "...", "token_type" => "Bearer", ... }

# Step 5 — Refresh when the access token expires
new_tokens = client.refresh_token(refresh_token: token_data['refresh_token'])

Supported SMART Client Types

client_type: Authentication When to use
:public (default) PKCE only Browser/mobile apps that cannot store a secret
:confidential_symmetric HTTP Basic Auth (client_secret) Server-side apps with a securely stored secret
:confidential_asymmetric JWT assertion (private_key_jwt, RS384/ES384) Server-side apps using a registered key pair

For a confidential asymmetric client, provide a private key and key ID:

client = Safire::Client.new(
  {
    base_url:    'https://fhir.example.com',
    client_id:   'my_client_id',
    redirect_uri: 'https://myapp.example.com/callback',
    scopes:      ['openid', 'profile', 'patient/*.read'],
    private_key: OpenSSL::PKey::RSA.new(File.read('private_key.pem')),
    kid:         'my-key-id-123'
  },
  client_type: :confidential_asymmetric
)
# Authorization and token exchange are identical — Safire builds the JWT assertion automatically

Configuration

Safire.configure do |config|
  config.logger   = Rails.logger   # Default: $stdout
  config.log_http = true           # Log HTTP requests (sensitive headers always filtered)
end

See the Configuration Guide for all options including user_agent, log_level, and SSL settings.


Demo Application

A Sinatra-based demo is included in examples/sinatra_app/:

bin/demo
# Visit http://localhost:4567

Demonstrates SMART discovery, all authorization flows, and token refresh. See examples/sinatra_app/README.md for details.


Development

bin/setup            # Install dependencies
bundle exec rspec    # Run tests
bin/console          # Interactive prompt

To serve the docs locally:

bin/docs
cd docs && bundle install && bundle exec jekyll serve
# Visit http://localhost:4000/safire/

Contributing

Bug reports and pull requests are welcome. Please read CONTRIBUTION.md before opening a PR — it covers branch naming, commit message style, and the sign-off requirement.


License

Available as open source under the Apache 2.0 License.


Parts of this project were developed with AI assistance (Claude Code) and reviewed by maintainers.