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.