Ctrl K
ComunidadGuías…

lib/tasks/oauth.rake

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
namespace :oauth do
  # The registry (Oauth::FirstPartyClients): the personal access client, and
  # each hosted MCP server whose resource FIZZY_OAUTH_MCP_RESOURCES names here.
  # A secret is never minted or rotated; a client without one is named, for
  # oauth:mint_client_secret. DRY_RUN=1 prints what each client would change
  # and writes nothing.
  desc "Create or converge Fizzy's first-party OAuth clients, all in one transaction (DRY_RUN=1 to print the changes and write nothing)"
  task provision_first_party_clients: :environment do
    if ActiveModel::Type::Boolean.new.cast(ENV["DRY_RUN"].presence)
      puts "Dry run: nothing is written."

      Oauth::FirstPartyClients.plan.each do |client|
        changes = client.changes.except("client_id")
        puts "#{client.client_id}: #{client.new_record? ? "would be created" : changes.any? ? "would converge" : "unchanged"}"

        changes.each do |attribute, (from, to)|
          from, to = [ from, to ].map { it && "[digest]" } if attribute == "client_secret_digest"
          puts "  #{attribute}: #{from.inspect} -> #{to.inspect}"
        end

        if client.invalid?
          puts "  #{client.client_id} wouldn't save: #{client.errors.full_messages.to_sentence}"
        end

        if Oauth::FirstPartyClients.awaiting_secret?(client)
          puts "  #{client.client_id} would have no secret yet, for oauth:mint_client_secret[#{client.client_id}]"
        end
      end
    else
      Oauth::FirstPartyClients.provision!.each do |client|
        puts "#{client.client_id}: #{client.previously_new_record? ? "created" : "converged"}"

        if Oauth::FirstPartyClients.awaiting_secret?(client)
          puts "  #{client.client_id} has no secret yet. Give it one with: bin/rails \"oauth:mint_client_secret[#{client.client_id}]\""
        end
      end
    end
  rescue ArgumentError => error
    abort error.message
  end

  # For an MCP server the registry doesn't host, such as a self-hosted
  # install's. Its client converges over the registry's baseline, as a
  # hosted one does.
  desc "Create or converge an MCP server's OAuth client outside the registry (RESOURCE=https://mcp.example.com/mcp, or the one FIZZY_OAUTH_MCP_RESOURCES names; CLIENT_ID=… for each resource past the first)"
  task provision_mcp_client: :environment do
    resource = ENV["RESOURCE"].presence || (Oauth.mcp_resources.one? ? Oauth.mcp_resources.first : nil)

    if resource.nil?
      abort "Name the MCP server's resource with RESOURCE=…, or configure exactly one in FIZZY_OAUTH_MCP_RESOURCES"
    elsif !Oauth.mcp_resource?(resource)
      abort "#{resource} isn't in FIZZY_OAUTH_MCP_RESOURCES, so its client couldn't exchange tokens"
    else
      client = Oauth::FirstPartyClients.provision_mcp_server!(resource, client_id: ENV["CLIENT_ID"].presence)
      puts "#{client.client_id} fronts #{client.protected_resource}. Give it a secret with: bin/rails \"oauth:mint_client_secret[#{client.client_id}]\""
    end
  rescue ArgumentError => error
    abort error.message
  end

  desc "Mint a new secret for an operator-provisioned confidential client, printed once; the old one stops working"
  task :mint_client_secret, [ :client_id ] => :environment do |_task, args|
    client = Oauth::Client.find_by!(client_id: args.fetch(:client_id))
    secret = client.rotate_secret!

    puts "Secret for #{client.client_id} (shown once, store it now):"
    puts secret
  end
end