Skip to content

How to use Azure Managed Identity for authentication and authorization for Ruby On Rails Active Storage? #233

@avivansh

Description

@avivansh

Current setup

azure:
  service: AzureStorage
  storage_account_name: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-NAME") %>
  storage_access_key: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-ACCESS-KEY") %>
  container: <%= VaultService.get_secret('AZURE-ATTACHMENTS-BUCKET-NAME') %>

Given the implementation of accessing Azure Storage using Access Token. Link

require "azure/storage/common"

access_token = <your initial access token>

# Creating an instance of `Azure::Storage::Common::Core::TokenCredential`
token_credential = Azure::Storage::Common::Core::TokenCredential.new access_token
token_signer = Azure::Storage::Common::Core::Auth::TokenSigner.new token_credential
blob_token_client = Azure::Storage::Blob::BlobService.new(storage_account_name: <your_account_name>, signer: token_signer)

Given the implementation of Active Storage for Ruby on Rails. It uses azure-storage-blob gem under the hood.
link

    def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
      @client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
      @signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
      @container = container
      @public = public
    end

New Setup, config/storage.yml

azure:
  service: AzureStorage
  storage_account_name: <%= VaultService.get_secret("AZURE-STORAGE::AZURE-STORAGE-ACCOUNT-NAME") %>
  container: <%= VaultService.get_secret('AZURE-ATTACHMENTS-BUCKET-NAME') %>

Monkey patched to use the above information to use active storage using managed identity

module ActiveStorage
  class Service::AzureStorageService < Service
    def initialize(storage_account_name:, container:, public: false, **options)
      access_token = AzureAd::ManagedIdentityTokenProvider.new('https://storage.azure.com/', client_id: ENV['AKS_MANAGED_IDENTITY_ID']).get_authentication_header.split(' ').last
      # Creating an instance of `Azure::Storage::Common::Core::TokenCredential`
      token_credential = ::Azure::Storage::Common::Core::TokenCredential.new access_token
      token_signer = ::Azure::Storage::Common::Core::Auth::TokenSigner.new token_credential
      @client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, signer: token_signer, **options)
      user_delegation_key = @client.get_user_delegation_key(DateTime.now - 1.minute, DateTime.now + 6.day + 23.hours)
      @signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name: storage_account_name, user_delegation_key: user_delegation_key )
      @container = container
      @public = public
    rescue StandardError => e
      raise e unless Rake.respond_to?(:application) && (!Rake.application.top_level_tasks.exclude?('assets:precompile') || !Rake.application.top_level_tasks.exclude?('source_map:upload_source_map'))
    end
  end
end

Is this approach correct? (I am yet to test this.). Also, is there any other approach on how to achieve this? I have posted the question on stackoverflow as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions