Created
March 4, 2021 10:48
-
-
Save caendekerk/f8291bf49ea197b3665ddd6d9befc31b to your computer and use it in GitHub Desktop.
Rails API only with Okta but without Devise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative 'boot' | |
require 'rails' | |
# Pick the frameworks you want: | |
require 'active_model/railtie' | |
require 'active_job/railtie' | |
require 'active_record/railtie' | |
require 'action_controller/railtie' | |
require 'action_mailer/railtie' | |
require 'action_view/railtie' | |
# Require the gems listed in Gemfile, including any gems | |
# you've limited to :test, :development, or :production. | |
Bundler.require(*Rails.groups) | |
module APP_Api | |
class Application < Rails::Application | |
# Initialize configuration defaults for originally generated Rails version. | |
config.load_defaults 6.0 | |
# Settings in config/environments/* take precedence over those specified here. | |
# Application configuration can go into files in config/initializers | |
# -- all .rb files in that directory are automatically loaded after loading | |
# the framework and any gems in your application. | |
# Only loads a smaller set of middleware suitable for API only apps. | |
# Middleware like session, flash, cookies can be added back manually. | |
# Skip views, helpers and assets when generating a new resource. | |
config.api_only = true | |
# https://github.com/omniauth/omniauth#integrating-omniauth-into-your-rails-api | |
config.session_store :cookie_store, key: '_APP_session', expire_after: 1.day | |
config.middleware.use ActionDispatch::Cookies | |
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options | |
# Use :sql format instead of default :ruby in order to support PostgreSQL specific features | |
# such as views. See https://guides.rubyonrails.org/active_record_migrations.html#types-of-schema-dumps. | |
config.active_record.schema_format = :sql | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApplicationController < ActionController::API | |
include ActionController::Caching | |
before_action :authenticate_user! | |
def authenticate_user! | |
jwt =JwtAuthService.decode(request_token) | |
user_id = jwt['user_id'] | |
@current_user = User.find(user_id) | |
rescue JWT::VerificationError, JWT::DecodeError, ActiveRecord::RecordNotFound | |
raise Exception.new(status: 401) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
class AuthenticationController < ApplicationController | |
include ActionController::RequestForgeryProtection | |
include ActionController::Cookies | |
include AbstractController::Rendering | |
include ActionView::Layouts | |
skip_before_action :authenticate_user! | |
# SECURITY: Must not be accessible across origins (from untrusted domains) | |
def csrf_token | |
render json: { csrfToken: form_authenticity_token } | |
end | |
def callback | |
user = User.create_or_initialize_by(email: request.env['omniauth.auth'].dig('info', 'email')&.downcase) | |
if user | |
session[:okta] = { user_id: user.id, expires: 30.seconds.since } | |
redirect_to '/login?submit=1' | |
else | |
raise Exception.new(status: 401, title: 'User is not authorized') | |
end | |
end | |
def create | |
user = | |
if session[:okta] && session[:okta].fetch(:expires, 0) > Time.zone.now | |
User.find_by(id: session[:okta][:user_id]) | |
end | |
if user | |
render json: { jwt: JwtAuthService.encode({ user_id: user.id }) } | |
else | |
raise Exception.new(status: 401, title: 'No active session found') | |
end | |
end | |
private | |
def auth | |
request.env['omniauth.auth'] | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source 'https://rubygems.org' | |
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' | |
gem 'rails', '~> 6.0.0' | |
# Shorten boot time | |
gem 'bootsnap' | |
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder | |
gem 'jbuilder', github: 'rails/jbuilder' | |
gem 'oj' | |
# Authentication | |
gem 'jwt' | |
gem 'omniauth-okta', github: 'dandrews/omniauth-okta' | |
gem 'omniauth-rails_csrf_protection' | |
# PostgreSQL | |
gem 'pg' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'jwt' | |
def encode(payload) | |
payload = payload.dup | |
payload['exp'] = tomorrow.to_i | |
JWT.encode(payload, Rails.application.secrets.secret_key_base) | |
end | |
def decode(token) | |
decoded_token = JWT.decode(token, Rails.application.secrets.secret_key_base) | |
decoded_token.first | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="login" | |
[ngClass]="{'loading': isLoading}"> | |
<form #oktaForm ngNoForm action="/auth/okta" method="POST" (submit)="isLoading=true"> | |
<input type="hidden" name="authenticity_token" [value]="csrfToken"> | |
<button class="login__button" | |
[disabled]="!csrfToken" | |
(click)="onSubmit(oktaForm)"> | |
Login with Okta | |
</button> | |
</form> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LoginPage { | |
csrfToken: string | |
isLoading = false | |
login() { | |
# Pseudocode | |
getCsrfToken(): Observable<{csrfToken: string}> { | |
return this.api.request('GET', '/auth/csrf_token') | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
return if (Rails.env.test? || Rails.env.e2e?) | |
require 'omniauth-okta' | |
Rails.application.config.middleware.use( | |
OmniAuth::Strategies::Okta, | |
ENV.fetch('OKTA_CLIENT_ID'), | |
ENV.fetch('OKTA_CLIENT_SECRET'), | |
scope: 'email groups', | |
fields: %w[email groups], | |
client_options: { | |
site: ENV.fetch('OKTA_ISSUER'), | |
authorize_url: ENV.fetch('OKTA_ISSUER') + '/v1/authorize', | |
token_url: ENV.fetch('OKTA_ISSUER') + '/v1/token', | |
user_info_url: ENV.fetch('OKTA_ISSUER') + '/v1/userinfo', | |
redirect_uri: ENV.fetch('OKTA_REDIRECT_URI') | |
} | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
get '/auth/csrf_token', to: 'authentication#csrf_token' | |
get '/auth/okta/callback', to: 'authentication#callback' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very helpful! Thanks