Skip to content

Latest commit

 

History

History
152 lines (107 loc) · 7.37 KB

UPGRADING.md

File metadata and controls

152 lines (107 loc) · 7.37 KB

Upgrading from omniauth-azure-activedirectory-v2

This guide assumes you were on v2.3 or v2.4 of the old-named gem. The basic steps are:

  • Update your code to account for the rename
  • Update your code to account for breaking changes

Updates due to the gem rename

All gem users will likely need to follow these steps.

  • In general, searching project-wide for azure_activedirectory_v2 and replacing with entra_id and, likewise, for the hyphenated azure-activedirectory-v2 and replacing with entra-id, will cover a lot of use cases
  • README.md always included examples with environment variables that were named as illustrations only; these have changed from e.g. AZURE_CLIENT_ID to ENTRA_CLIENT_ID just for internal consistency, but while renaming your own related environment variables or constants (should you use any) may help with code base understanding and consistency, it's not essential. Those names are part of your code base, not part of code in this gem.

Configuration

Rename the strategy in your configuration block:

config.omniauth(
  :azure_activedirectory_v2,
  # ...
)

...becomes:

config.omniauth(
  :entra_id,
  # ...
)

Callback routes

Depending on how you handle callbacks from OmniAuth, you might need to update routes or controllers handling shared routes to account for the name change. The old callback URL of:

https://example.com/v1/auth/azure_activedirectory_v2/callback

...is now:

https://example.com/v1/auth/entra_id/callback

URL generation

Change things like this:

omniauth_authorize_url('resource_name_eg_user', 'azure_activedirectory_v2', scope: '...')

...to this:

omniauth_authorize_url('resource_name_eg_user', 'entra_id', scope: '...')

Updates due to other breaking changes

Change affecting all gem users

UIDs will change

This change is for UIDs and is the main reason for creating a V3 gem, whether or not it included the Entra name change.

  • The UID returned by OmniAuth for a user previously depended upon the oid (object ID) returned by Microsoft. As noted in #33 and fixed in #34, this might not be unique and tenant ID (tid) is supposed to be considered too.
  • Out-of-box, Entra ID will do this. If you were an Azure ActiveDirectory V2 (old-name gem, version 2.x) user, then you will have been receiving different UIDs based only on the oid from Microsoft.
  • The change of OID might break the connection between a previously-registered and logged in user and a new login as usually, you need to store the OmniAuth UID somewhere alongside or within your User records when a user is "connected to" an external OAuth service such as Entra ID.

You have two options, should the issue affect you (and it almost certainly will).

  • If you can determine the tenant IDs for all users in your database, you can just migrate the UIDs. The new UID is just a simple concatenation of tenant ID and object ID, so treating the UID as a string, add the tenant ID as a prefix without any other changes in your migration and things should work fine thereafter.
  • Otherwise, you should lazy-migrate:
    • As usual, in your OAuth callback handler, request.env['omniauth.auth'].uid gives the UID - but now that's the "new" Entra gem's value which includes tenant ID.
    • If you can find a user with that ID, then all good - they've been migrated already or got connected to Entra after you started using the updated gem
    • Otherwise, check request.env['omniauth.auth'].extra.dig('raw_info', 'oid') - this gives the value that the old Azure ActiveDirectory V2 gem used as UID
    • Look up the user with this ID. If you find them, great; remember to migrate their record by updating their stored auth ID to the new request.env['omniauth.auth'].uid value.
    • For better security add something like an indexed boolean column indicating whether or not the user has been thus migrated and only perform old OID lookups on users which have not yet been migrated.
    • If the user can't be found by either means, then they've not been connected to your system yet. Your existing handling path for such a condition applies.

Applications that handle multiple OAuth providers

If your user records contain users that have 'connected' to more than one kind of OAuth provider, then as well as the third party's UID being stored for future logins, you'll most likely have stored the OmniAuth provider name too so that the UID can be looked up in a provider's context (there's no guarantee, of course, that UIDs are unique between providers since they're entirely independent entities with their own strategies for allocating unique IDs).

In that case, you will need to migrate records from the old azure_activedirectory_v2 name to entra_id. Zero-downtime deployment of this change would be very hard since your codebase would need to update from the Azure ActiveDirectory V2 gem to the Entra ID gem with the migration running simultaneously, so if you need to do such a migration, then you probably should plan for a small maintenance window. At the scheduled time, go into maintenance mode, migrate, deploy, and restore normal service. Even without this, though, the 'worst that can happen' (in theory!) would be temporary user login failures. Either the Entra gem will be causing you to look for a user with an entra_id provider but the migration to set this hasn't run yet, or the other way round, with the old gem looking for the old provider name but it's already updated.

Example migration code

Suppose you support multiple providers and your User table stores their chosen provider in a column auth_provider, with their UID in auth_uid. An (irreversible) database migration might do this:

def up
  add_column :users, :migrated_to_entra, :boolean

  User
    .where(auth_provider: 'azure_activedirectory_v2')
    .update_all(
      auth_provider:     'entra_id',
      migrated_to_entra: false
    )
end

def down
  raise ActiveRecord::IrreversibleMigration
end

This means the migrated_to_entra column is only ever false for existing users that were linked using the V2 gem. Everything else will be at NULL. Now you lazy-move those users to the new UID format in the code you use to look up a user in your OmniAuth OAuth 2 callback handler, e.g. via a method such as this:

# Here, "raw_info" comes from e.g.:
#
#   request.env['omniauth.auth'].extra&.dig('raw_info') || {}
#
def find_user_for_omniauth(auth_provider:, auth_uid:, raw_info:)
  found_user = User.find_by(
    auth_provider: auth_provider,
    auth_uid:      auth_uid
  )

  if found_user.nil? && auth_provider == 'entra_id'
    found = User.find_by(
      auth_provider:     'entra_id',
      auth_uid:          raw_info['oid'],
      migrated_to_entra: false
    )

    if found
      found.update_columns(
        auth_uid:          auth_uid,
        migrated_to_entra: true
      )
    end
  end

  return found_user
end

Once there are no rows with a migrated_to_entra value of false for active users, you will be able to drop the column and remove the lazy migration code.

Other changes that might affect you

  • If you refer to OmniAuth::Strategies::AzureActivedirectoryV2 at all, then this becomes OmniAuth::Strategies::EntraId (note lower case "d").
  • base_azure_url option renamed to just base_url with corresponding rename of OmniAuth::Strategies::AzureActivedirectoryV2::BASE_AZURE_URL to OmniAuth::Strategies::EntraId::BASE_URL.