Posso expor uma rota sem autenticação?

Existe alguma maneira de criar uma rota que ignore a autenticação?

Isso é usado para integrar com serviços de terceiros.
O requisito é fornecer ao serviço uma URL (cabeçalhos ou payloads de qualquer tipo não são suportados).
Antes, isso era possível adicionando “?api_key=…&api_username=” ao final, mas essa opção foi removida.

A instância do Discourse é hospedada por nós.

Você precisaria de um plugin personalizado que crie uma rota capaz de aceitar um payload e agir sobre ele. Esteja ciente de que, como não é possível autenticar o usuário, você pode precisar de uma forma de autenticar o payload.

Minha regra é não permitir contas anônimas. Portanto, não consigo acessar as rotas do plugin. O que eu precisaria é de uma maneira de criar uma rota para esse tipo de cenário.

Por exemplo,

  # O Rails 3.2 padrão permite que a requisição passe com uma sessão em branco
  # Aqui estamos sendo mais rigorosos e anulando a sessão / current_user
  # e depois lançando uma exceção de CSRF
  def handle_unverified_request
    # NOTA: A chave de API é secreta; tê-la invalida a necessidade de um token CSRF
    unless is_api? || is_user_api?
      super
      clear_current_user
      render plain: "[\"BAD CSRF\"]", status: 403
    end
  end

Uma maneira de notificar, durante esse fluxo ou em todo o fluxo de autenticação, que um controlador e uma ação recebidos via parâmetros (definidos automaticamente ao chamar uma rota) podem contornar tudo e executar a rota.

Não tenho plena consciência de como tudo funciona, mas como existe uma opção de acesso anônimo, tenho certeza de que há uma maneira de fazer isso.

Isso permitiria que as pessoas criptografassem a carga útil, se necessário, mas para atualizações genéricas de informações no sistema, seria útil.

Através de um plugin, você pode adicionar sua nova rota ao DiscoursePluginRegistry.api_parameter_routes para que possa continuar passando credenciais de API via parâmetros de consulta:

Você também precisará incluir o bloco

skip_before_action :redirect_to_login_if_required

no seu Controller personalizado, caso este seja um site com login obrigatório.

Ok. Então, sobrescreva esse método e pule a ação. Vou tentar e avisar vocês se conseguir.

# Por padrão, permitimos apenas cabeçalhos para envio de credenciais de API
  # No entanto, em alguns cenários, é essencial enviá-las por parâmetros de URL
  # então precisamos adicionar algumas exceções
  def api_parameter_allowed?
    request_method = @env["REQUEST_METHOD"]&.downcase&.to_sym
    request_format = @env['action_dispatch.request.formats']&.first&.symbol

    path_params = @env['action_dispatch.request.path_parameters']
    request_route = "#{path_params[:controller]}##{path_params[:action]}" if path_params

    if 'm_exposed#endpoint' == request_route && request_method == 'get'
      puts 'Allowed'
      return true
    end

    PARAMETER_API_PATTERNS.any? do |p|
      (p[:method] == "*" || Array(p[:method]).include?(request_method)) &&
      (p[:format] == "*" || Array(p[:format]).include?(request_format)) &&
      (p[:route] == "*" || Array(p[:route]).include?(request_route))
    end
  end

Fiz isso apenas para minimizar os diffs do git no futuro.

Além disso, meu controlador:

# frozen_string_literal: true

class MExposedController < ::ApplicationController

  skip_before_action :redirect_to_login_if_required, raise: false

  def endpoint
    render json: { state: "hello world" }
  end

end

Então, agora o endpoint não verifica as credenciais nos parâmetros ou nos cabeçalhos.

Como faço para que ele verifique a chave de API fornecida e permita o acesso apenas se for válida? (Como funcionava antes)

@riking @blake?

Acredito que adicionar requires_login ao seu controlador resolverá o problema. Dê uma olhada em alguns dos outros controladores em app/controllers para exemplos.

Bom, isso não funcionou :slight_smile:

class MExposedController < ::ApplicationController

  requires_login

  skip_before_action :redirect_to_login_if_required, raise: false

Concluído 403 Proibido

{"errors":["Você precisa estar logado para fazer isso."],"error_type":"not_logged_in"}

Isso está declarado dentro de um plugin.

E você passou suas credenciais da API via parâmetros de consulta? Se sim, isso significa que sua rota provavelmente não foi adicionada corretamente à lista de exceções para permitir credenciais da API nos parâmetros de consulta. Talvez seja necessário depurar a lógica da lista de exceções e verificar se sua rota foi adicionada.

Bom, funciona e contorna tudo sem exigir o login. Eu postei como fiz. Hmm..

Parece que só consigo fazer funcionar sem credenciais. Bem, pelo menos resolvi meu problema inicial.