New API Endpoint: Programmatically Add Authentication Identity for Users

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

User Story

As an administrator, I want to programmatically add an authentication identity for a user so that users logging in with one identity provider automatically receive the other.

Problem:

Currently, we have both SAML (Azure/Entra) and LDAP configured, but there is no way to programmatically add an authentication identity for a user upon creation. This means that users logging in with one provider do not automatically receive the other, which disrupts group syncs and nightly LDAP user syncs. The existing SAML config option to sync users’ LDAP on login does not work for us because it requires mapping the UID between providers, which is not possible due to our identity system's architecture.

Goal:

Implement a new API endpoint that allows administrators to add an authentication identity for a user programmatically, ensuring that users logging in with one identity provider receive the other.

Requirements & Acceptance Criteria

ID Requirement Acceptance Criteria
1 Create a new API endpoint for adding authentication identities The endpoint should allow admins to add an identity for a user if it does not already exist
2 Ensure security measures are in place Only administrators should be able to call this endpoint
3 Validate that the provider and extern_uid are correctly formatted The API should return an error if invalid inputs are provided
4 Log successful and failed API calls Logs should capture admin actions for audit purposes

Technical Details

Security

• Only administrators should be able to call this endpoint.

• Proper validation for extern_uid and provider should be in place.

Implementation

Introduce a new API endpoint in /lib/api/users.rb:

desc "Add a user's identity. Available only for admins" do
  success Entities::UserWithAdmin
end
params do
  requires :id, type: Integer, desc: 'The ID of the user'
  requires :provider, type: String, desc: 'The external provider'
  requires :extern_uid, type: String, desc: 'The external uid'
end
# rubocop: disable CodeReuse/ActiveRecord
post ":id/identities", feature_category: :system_access do
  authenticated_as_admin!

  user = User.find_by(id: params[:id])
  not_found!('User') unless user

  identity = user.identities.new(provider: params[:provider], extern_uid: params[:extern_uid])
  if identity.save
    present user, with: Entities::UserWithAdmin, current_user: current_user
  else
    render_validation_error!(identity)
  end
end
# rubocop: enable CodeReuse/ActiveRecord

Additional Notes

• This feature aligns with existing authentication workflows.

• Would be a strong candidate for the co-create program.

Edited by 🤖 GitLab Bot 🤖