github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/tests/system/lib/oauth/client.rb (about)

     1  module OAuth
     2    class Client
     3      attr_reader :client_id, :client_secret, :registration_token
     4  
     5      def self.create(inst)
     6        body = {
     7          redirect_uris: ["cozy://"],
     8          client_name: "test_#{Faker::Internet.slug}",
     9          software_id: "github.com/cozy/cozy-stack/tests/system"
    10        }
    11        opts = { content_type: :json, accept: :json }
    12        res = inst.client["/auth/register"].post body.to_json, opts
    13        body = JSON.parse(res.body)
    14        params = {
    15          client_id: body["client_id"],
    16          client_secret: body["client_secret"],
    17          registration_token: body["registration_access_token"]
    18        }
    19        new params
    20      end
    21  
    22      def initialize(opts)
    23        @client_id = opts[:client_id]
    24        @client_secret = opts[:client_secret]
    25        @registration_token = opts[:registration_token]
    26      end
    27  
    28      def register_passphrase(inst, pass)
    29        inst.passphrase = pass
    30        body = {
    31          client_id: @client_id,
    32          client_secret: @client_secret,
    33          register_token: inst.register_token,
    34          passphrase: inst.hashed_passphrase,
    35          iterations: 650_000,
    36          hint: "a hint to help me remember my passphrase",
    37          key: "dont_care_key",
    38          public_key: "dont_care_public_key",
    39          private_key: "dont_care_private_key"
    40        }
    41        opts = { content_type: :json, accept: :json }
    42        res = inst.client["/settings/passphrase/flagship"].post body.to_json, opts
    43        JSON.parse(res.body)["session_code"]
    44      end
    45  
    46      def open_authorize_page(inst, session_code)
    47        @authorize_params = [
    48          "session_code=#{session_code}",
    49          "client_id=#{@client_id}",
    50          "redirect_uri=cozy://",
    51          "response_type=code",
    52          "scope=*",
    53          "state=state",
    54          "code_challenge=LdAL134CIs7YgmZUganB2fkHMJ0W4F7QB6HqY5KEd6k", # "challenge"
    55          "code_challenge_method=S256"
    56        ].join('&')
    57        inst.client["/auth/authorize?#{@authorize_params}"].get
    58      end
    59  
    60      def receive_flagship_code(inst, timeout = 30)
    61        timeout.times do
    62          sleep 1
    63          received = Email.received kind: "to", query: inst.email
    64          received.reject! { |e| e.subject =~ /(New.connection|Nouvelle.connexion)/ }
    65          return extract_code received.first if received.any?
    66        end
    67        raise "Confirmation mail for moving was not received after #{timeout} seconds"
    68      end
    69  
    70      def extract_code(mail)
    71        scanned = mail.body.scan(/^(\d{6})$/)
    72        raise "No secret in #{mail.subject} - #{mail.body}" if scanned.empty?
    73        scanned.last.last
    74      end
    75  
    76      def validate_flagship(inst, authorize_page, code)
    77        opts = {
    78          max_redirects: 0,
    79          cookies: authorize_page.cookies
    80        }
    81        extracted = authorize_page.body.scan(/name="confirm-token" value="([^"]+)"/)
    82        body = { token: extracted.first.first, code: code }
    83        inst.client["/auth/clients/#{@client_id}/flagship"].post body, opts
    84        inst.client["/auth/authorize?#{@authorize_params}"].get(opts) do |response|
    85          params = extract_query_string response.headers[:location]
    86          params["code"]
    87        end
    88      end
    89  
    90      def access_token(inst, access_code)
    91        body = {
    92          grant_type: "authorization_code",
    93          code: access_code,
    94          client_id: @client_id,
    95          client_secret: @client_secret,
    96          code_verifier: "challenge"
    97        }
    98        opts = { accept: :json }
    99        res = inst.client["/auth/access_token"].post body, opts
   100        JSON.parse(res.body)
   101      end
   102  
   103      def extract_query_string(location)
   104        query = URI.parse(location).query
   105        CGI.parse(query).inject({}) do |h, (k, v)|
   106          h[k] = v.first
   107          h
   108        end
   109      end
   110  
   111      def list_permissions(inst, tokens)
   112        access_token = tokens["access_token"]
   113        opts = {
   114          accept: :json,
   115          authorization: "Bearer #{access_token}"
   116        }
   117        res = inst.client["/permissions/self"].get opts
   118        JSON.parse(res.body)
   119      end
   120  
   121      def create_session_code(inst)
   122        body = { passphrase: inst.hashed_passphrase }
   123        opts = { accept: :json, content_type: :json }
   124        begin
   125          res = inst.client["/auth/session_code"].post body.to_json, opts
   126        rescue RestClient::Exception => e
   127          token = JSON.parse(e.response.body)["two_factor_token"]
   128          code = inst.get_two_factor_code_from_mail
   129          body = {
   130            passphrase: inst.hashed_passphrase,
   131            two_factor_token: token,
   132            two_factor_passcode: code
   133          }
   134          res = inst.client["/auth/session_code"].post body.to_json, opts
   135        end
   136        body = JSON.parse(res.body)
   137        body["session_code"]
   138      end
   139  
   140      def destroy(inst)
   141        opts = { authorization: "Bearer #{@registration_token}" }
   142        inst.client["/auth/register/#{@client_id}"].delete opts
   143      end
   144    end
   145  end