# frozen_string_literal: true module PlatformSdk module Identity # Client for making calls to the Identity Server API class Client def initialize(identity_base_url, client_id, client_secret, auth_client = nil) @auth = auth_client.nil? ? AuthClient.new(identity_base_url, client_id, client_secret) : auth_client @conn = Faraday.new(identity_base_url) do |conn| conn.request :authorization, "Bearer", -> { @auth.auth_token } conn.request :retry conn.response :json conn.adapter :net_http end end def with_rescue yield rescue IdentityNotFoundError => e puts e end end # Client for getting auth tokens from identity server class AuthClient attr_accessor :conn, :token def initialize(base_url, client_id, client_secret) @client_id = client_id @client_secret = client_secret @conn = Faraday.new(base_url) do |conn| conn.request :url_encoded conn.response :raise_error conn.response :json conn.adapter :net_http end end def with_rescue yield rescue Faraday::ServerError => e raise_error_with_payload(IdentityServerError, e) rescue Faraday::ClientError => e raise_error_with_payload(IdentityClientError, e) end def post_payload(path, body) with_rescue do response = @conn.post(path, body) response.body.transform_keys!(&:to_sym) end end def auth_token @token = post_payload("/connect/token", request_body) if expired? @token[:access_token] end def expired? return true if @token.nil? token_expired?(@token[:access_token]) end def token_expired?(jwt) begin expiry_time = jwt_expiry_time(jwt) rescue JWT::ExpiredSignature return true end expiry_time <= Time.now.utc + 45 end def jwt_expiry_time(jwt) Time.at(JWT.decode(jwt, nil, false)[0]["exp"]) end def refresh_token_if_expired(jwt:, refresh_token:) raise ArgumentError if refresh_token.nil? || jwt.nil? return unless token_expired?(jwt) refresh_token(refresh_token) end def refresh_token(refresh_token) raise ArgumentError if refresh_token.nil? post_payload("/connect/token", request_body( grant_type: "refresh_token", refresh_token:)) end def refresh_session(session: {}) raise ArgumentError if session[:access_token].nil? || session[:refresh_token].nil? refreshed_tokens = refresh_token_if_expired(jwt: session[:access_token], refresh_token: session[:refresh_token]) return if refreshed_tokens.nil? session[:id_token] = refreshed_tokens[:id_token] session[:access_token] = refreshed_tokens[:access_token] session[:refresh_token] = refreshed_tokens[:refresh_token] end def raise_error_with_payload(exception_class, error) json_log = { exception: exception_class.new.class.name.demodulize, payload: error.response.dig(:request, :body), response_body: error.response[:body], status: error.response[:status] }.compact Rails.logger.info json_log.to_json if respond_to?(:Rails) raise exception_class, error.response end private def request_body(grant_type: "client_credentials", refresh_token: nil) return { grant_type:, client_id: @client_id, client_secret: @client_secret } if refresh_token.nil? { grant_type:, client_id: @client_id, client_secret: @client_secret, refresh_token: } end end end end