vendredi 1 mars 2019

Implementing omniauth strategy for authorization code grant

I'm attempting to use Omniauth to implement an authorization code grant-type through Okta. I've implemented a strategy that is nearly identical to that in the okta omniauth gem. I've added the necessary code to the devise initializer and it easily retrieves the authorization code and grant type form the authorization endpoint. However, when it returns the parameters to the redirect_uri, I don't understand how to get the strategy to initiate the callback_phase method which is necessary to exchange the code for an access token with the token endpoint. As a result, the auth hash is not created so the User.from_omniauth call in my controller throws an error.

Question: How do I deliver the access code to my strategy to retrieve the access token?

Any help would be greatly appreciated.

Strategy: require 'omniauth' require 'net/http'

# frozen_string_literal: true

require 'omniauth-oauth2'

module OmniAuth
  module Strategies
    class Moto < OmniAuth::Strategies::OAuth2

      ORG           = AUTH['oauth2']['moto']['OKTA_ORG']    || 'your-org'
      DOMAIN        = AUTH['oauth2']['moto']['OKTA_DOMAIN'] || "https://#{ORG}.okta.com"
      BASE_URL      = DOMAIN
      DEFAULT_SCOPE = %[openid profile email].freeze

      option :name, 'moto'

      option :skip_jwt, false
      option :jwt_leeway, 60
      option :redirect_uri, AUTH['oauth2']['moto']['redirect']

      option :client_options, {
        site:          BASE_URL,
        authorize_url: "#{BASE_URL}/oauth2/v1/authorize",
        token_url:     "#{BASE_URL}/oauth2/v1/token",
        response_type: 'authorization_code'
      }

      option :scope, DEFAULT_SCOPE

      uid { raw_info['sub'] }

      info do
        {
          name:       raw_info['name'],
          email:      raw_info['email'],
          first_name: raw_info['given_name'],
          last_name:  raw_info['family_name'],
          image:      raw_info['picture']
        }
      end

      extra do
        hash = {}
        hash[:raw_info] = raw_info unless skip_info?
        hash[:id_token] = access_token.token
        if !options[:skip_jwt] && !access_token.token.nil?
          hash[:id_info] = validated_token(access_token.token)
        end
        hash
      end

      alias :oauth2_access_token :access_token

      def access_token
        puts "in access token"
        ::OAuth2::AccessToken.new(client, oauth2_access_token.token, {
          :refresh_token => oauth2_access_token.refresh_token,
          :expires_in    => oauth2_access_token.expires_in,
          :expires_at    => oauth2_access_token.expires_at
        })
      end

      def raw_info
        @_raw_info ||= access_token.get('/oauth2/v1/userinfo').parsed || {}
      rescue ::Errno::ETIMEDOUT
        raise ::Timeout::Error
      end

      def request_phase
        puts "In request phase"
        super
      end

      def callback_phase
        puts "in callback phase"
        build_access_token
        super
      end

      def callback_url
        options[:redirect_uri] || (full_host + script_name + callback_path)
      end

      def validated_token(token)
        JWT.decode(token,
                   nil,
                   false,
                   verify_iss:        true,
                   iss:               BASE_URL,
                   verify_aud:        true,
                   aud:               BASE_URL,
                   verify_sub:        true,
                   verify_expiration: true,
                   verify_not_before: true,
                   verify_iat:        true,
                   verify_jti:        false,
                   leeway:            options[:jwt_leeway]
                   ).first
      end
    end
  end
end

controller callback

class OmniauthController < Devise::OmniauthCallbacksController
def moto_callback
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    puts "Request env #{env['omniauth.auth']}"
    logger.debug "Request env #{env['omniauth.auth']}"
    @user = User.from_omniauth(request.env["omniauth.auth"])
    print(@user)
    if @user.save
        session[:oktastate] = request.env["omniauth.auth"]
        print(@user.oauth_permissions(session[:oktastate]))
    else
        print(@user.errors.full_messages)
    end
    if @user.persisted?
        redirect_to "/users"
    end
end
end

initializer/devise.rb

config.omniauth(:moto, AUTH['oauth2']['moto']['OKTA_CLIENT_ID'], AUTH['oauth2']['moto']['OKTA_CLIENT_SECRET'], :scope => 'openid profile email', :fields => ['profile', 'email'], :strategy_class => OmniAuth::Strategies::Moto)

Aucun commentaire:

Enregistrer un commentaire