PHR Profile Module Implementation Plan
Overview
This document outlines the implementation plan for the PHR Profile module in the ABDM system. The module handles user profile management including viewing, updating, and managing ABHA number linkages. Some APIs are already implemented (Get Profile, QR Code, ABHA Card) and need to be extended with additional functionality.
Key Architecture Decision: Following existing ABDM system patterns, this implementation uses a stateless architecture with no database persistence for sessions or transactions. All state management is handled by the frontend via API responses.
Existing Implementation Status
Already Implemented APIs
- Get Profile -
FetchPatientProfileservice - Get QR Code -
FetchPatientQrCodeservice - Get ABHA Card -
FetchPatientCardservice
All existing implementations follow these patterns:
- Stateless design: No database storage for API responses or session data
- X-Token authentication in headers
- AbdmRequest logging for all API calls
- HTTParty for API communication
- Service classes in
parts/abdm/services/abdm/abha/profile/ - Interactors in
parts/abdm/interactors/abdm/abha/patient_methods/ - Controller in
parts/abdm/controllers/abdm/abha/patient_profiles_controller.rb
APIs to Implement (Based on Postman Collection)
1. Update Profile
Update basic profile information (name, DOB, gender, address)
- Endpoint:
POST /profile/updateProfile
2. Update Email
Two-step process with OTP verification
- Send OTP:
POST /profile/updateEmail/sendOtp - Verify OTP:
POST /profile/updateEmail/verifyOtp
3. Update Mobile
Two-step process with OTP verification
- Send OTP:
POST /profile/updateMobile/sendOtp - Verify OTP:
POST /profile/updateMobile/verifyOtp
4. Update Password
Direct password update with current password verification
- Endpoint:
POST /profile/updatePassword
5. Link ABHA Number
Multiple flows for linking ABHA numbers:
Via ABHA OTP:
- Send OTP:
POST /profile/linkAbha/sendOtp - Verify OTP:
POST /profile/linkAbha/verifyOtp - Link Request:
POST /profile/linkAbha/linkRequest
Via Aadhaar OTP:
- Send OTP:
POST /profile/linkAadhaar/sendOtp - Verify OTP:
POST /profile/linkAadhaar/verifyOtp - Link Request:
POST /profile/linkAadhaar/linkRequest
6. Switch Profile
Switch between multiple linked profiles
- Switch Profile:
POST /profile/switch - Verify Switch:
POST /profile/verifySwitch
7. Logout
Terminate user session
- Endpoint:
POST /logout
EXCLUDED from Implementation:
- PHR Certificate API
- Refresh Token API
Database Schema
No Database Changes Required - Following the existing ABDM stateless architecture:
- No new tables needed
- No new columns in existing tables
- No session/token persistence
- No transaction tracking in database
- All state managed by frontend via API responses
API Endpoints
Routes Configuration
# parts/abdm/routes.rb - Extend existing patients resource
resources :patients,
only: %i[],
controller: 'patient_profiles' do
collection do
# Existing endpoints (already implemented)
post :profile
post :qr_code
post :card
# New endpoints - Profile Management
post :update_profile
# Email Update Flow
post :update_email_send_otp
post :update_email_verify_otp
# Mobile Update Flow
post :update_mobile_send_otp
post :update_mobile_verify_otp
# Password Management
post :update_password
# ABHA Number Linking - ABHA OTP Flow
post :link_abha_send_otp
post :link_abha_verify_otp
post :link_abha_request
# ABHA Number Linking - Aadhaar OTP Flow
post :link_aadhaar_send_otp
post :link_aadhaar_verify_otp
post :link_aadhaar_request
# Profile Switching
post :switch_profile
post :verify_switch_profile
# Session Management
post :logout
end
endService Classes Implementation
Directory Structure
parts/abdm/services/abdm/abha/profile/
├── existing/
│ ├── fetch_patient_profile.rb # ✅ Already exists
│ ├── fetch_patient_qr_code.rb # ✅ Already exists
│ └── fetch_patient_card.rb # ✅ Already exists
└── new/
├── update_profile.rb # 📝 To implement
├── email/
│ ├── send_otp_for_email.rb # 📝 To implement
│ └── verify_otp_for_email.rb # 📝 To implement
├── mobile/
│ ├── send_otp_for_mobile.rb # 📝 To implement
│ └── verify_otp_for_mobile.rb # 📝 To implement
├── password/
│ └── update_password.rb # 📝 To implement
├── linking/
│ ├── send_abha_otp_for_link.rb # 📝 To implement
│ ├── verify_abha_otp_for_link.rb # 📝 To implement
│ ├── send_aadhaar_otp_for_link.rb # 📝 To implement
│ ├── verify_aadhaar_otp_for_link.rb # 📝 To implement
│ ├── abha_link_request.rb # 📝 To implement
│ └── aadhaar_link_request.rb # 📝 To implement
├── switch/
│ ├── switch_profile.rb # 📝 To implement
│ └── verify_switch_profile.rb # 📝 To implement
└── logout.rb # 📝 To implementService Class Template (Following Existing Pattern)
module Abdm
module Abha
module Profile
class ServiceName
def self.perform(params, user_id)
token = Abdm::FetchGatewayTokenV3.perform
url = "#{Rails.application.credentials.abdm_phr_host}/profile/..."
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'X-Token' => "Bearer #{params[:x_token]}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = build_request_body(params)
begin
result = HTTParty.post(url, body: body.to_json, headers: headers)
rescue StandardError => e
error = e.to_s
end
# Log Request (Following existing AbdmRequest pattern)
abdm_request = AbdmRequest.create!(
url: url,
payload: body,
requested_by_id: user_id,
requested_by_type: 'User'
)
# Handle Response (Following existing pattern)
if result.present? && result.success?
abdm_request.update!(
response: result.parsed_response,
status_code: result.code,
success: true
)
else
abdm_request.update!(
response: error || result&.parsed_response,
status_code: result&.code,
success: false
)
end
result
end
private
def self.build_request_body(params)
# Implementation specific to each service
end
end
end
end
endInteractor Classes Implementation
Directory Structure
parts/abdm/interactors/abdm/abha/patient_methods/
├── existing/
│ └── profile.rb # ✅ Already exists
└── new/
├── update_profile.rb # 📝 To implement
├── update_email.rb # 📝 To implement (handles both send/verify)
├── update_mobile.rb # 📝 To implement (handles both send/verify)
├── update_password.rb # 📝 To implement
├── link_abha.rb # 📝 To implement (handles ABHA OTP flow)
├── link_aadhaar.rb # 📝 To implement (handles Aadhaar OTP flow)
├── switch_profile.rb # 📝 To implement (handles switch/verify)
└── logout.rb # 📝 To implementInteractor Template (Following Existing Pattern)
module Abdm
module Abha
module PatientMethods
class InteractorName < BaseInteractor
def call
require_context(:x_token, :other_required_params)
validate_input!
process_request!
# Success message
context.message = 'Success message'
end
private
def validate_input!
# Input validation logic specific to each interactor
end
def process_request!
result = Abdm::Abha::Profile::ServiceName.perform(
context.to_h,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
else
error_response = result&.parsed_response || { error: 'Operation failed' }
context.message = error_response['message'] || error_response['error'] || 'Operation failed'
context.fail!
end
end
end
end
end
endController Implementation
PatientProfilesController Updates
module Abdm
module Abha
class PatientProfilesController < ApiController
before_action :policy_check
# Existing actions (already implemented)
def profile
# ✅ Already exists
end
def qr_code
# ✅ Already exists
end
def card
# ✅ Already exists
end
# NEW ACTIONS TO IMPLEMENT:
# Profile Update
def update_profile
result = PatientMethods::UpdateProfile.call(
update_profile_params.merge(x_token: extract_x_token)
)
render_result(result)
end
# Email Update Flow
def update_email_send_otp
result = PatientMethods::UpdateEmail.call(
email_send_params.merge(action: 'send_otp', x_token: extract_x_token)
)
render_result(result)
end
def update_email_verify_otp
result = PatientMethods::UpdateEmail.call(
email_verify_params.merge(action: 'verify_otp', x_token: extract_x_token)
)
render_result(result)
end
# Mobile Update Flow
def update_mobile_send_otp
result = PatientMethods::UpdateMobile.call(
mobile_send_params.merge(action: 'send_otp', x_token: extract_x_token)
)
render_result(result)
end
def update_mobile_verify_otp
result = PatientMethods::UpdateMobile.call(
mobile_verify_params.merge(action: 'verify_otp', x_token: extract_x_token)
)
render_result(result)
end
# Password Update
def update_password
result = PatientMethods::UpdatePassword.call(
password_params.merge(x_token: extract_x_token)
)
render_result(result)
end
# ABHA Link Flow (ABHA OTP)
def link_abha_send_otp
result = PatientMethods::LinkAbha.call(
abha_send_params.merge(action: 'send_otp', x_token: extract_x_token)
)
render_result(result)
end
def link_abha_verify_otp
result = PatientMethods::LinkAbha.call(
abha_verify_params.merge(action: 'verify_otp', x_token: extract_x_token)
)
render_result(result)
end
def link_abha_request
result = PatientMethods::LinkAbha.call(
link_request_params.merge(action: 'link_request', x_token: extract_x_token)
)
render_result(result)
end
# ABHA Link Flow (Aadhaar OTP)
def link_aadhaar_send_otp
result = PatientMethods::LinkAadhaar.call(
aadhaar_send_params.merge(action: 'send_otp', x_token: extract_x_token)
)
render_result(result)
end
def link_aadhaar_verify_otp
result = PatientMethods::LinkAadhaar.call(
aadhaar_verify_params.merge(action: 'verify_otp', x_token: extract_x_token)
)
render_result(result)
end
def link_aadhaar_request
result = PatientMethods::LinkAadhaar.call(
link_request_params.merge(action: 'link_request', x_token: extract_x_token)
)
render_result(result)
end
# Profile Switch Flow
def switch_profile
result = PatientMethods::SwitchProfile.call(
switch_params.merge(action: 'switch', x_token: extract_x_token)
)
render_result(result)
end
def verify_switch_profile
result = PatientMethods::SwitchProfile.call(
verify_switch_params.merge(action: 'verify', x_token: extract_x_token)
)
render_result(result)
end
# Logout
def logout
result = PatientMethods::Logout.call(x_token: extract_x_token)
render_result(result)
end
private
def extract_x_token
request.headers['X-Token']&.gsub('Bearer ', '')
end
def update_profile_params
params.permit(
:firstName, :middleName, :lastName,
:dayOfBirth, :monthOfBirth, :yearOfBirth,
:gender, :email, :mobile, :countryCode,
address: [:addressLine, :country, :district, :pincode, :state, :town]
)
end
def email_send_params
params.permit(:email, :loginHint, :otpSystem, :scope)
end
def email_verify_params
params.permit(:transactionId, :otp, :authMethods, :scope)
end
def mobile_send_params
params.permit(:mobile, :loginHint, :otpSystem, :scope)
end
def mobile_verify_params
params.permit(:transactionId, :otp, :authMethods, :scope)
end
def password_params
params.permit(:password, :abhaAddress, :authMethods, :scope)
end
def abha_send_params
params.permit(:abhaNumber, :loginHint, :otpSystem, :scope)
end
def abha_verify_params
params.permit(:transactionId, :otp, :authMethods, :scope)
end
def aadhaar_send_params
params.permit(:loginId, :loginHint, :otpSystem, :scope)
end
def aadhaar_verify_params
params.permit(:transactionId, :otp, :authMethods, :scope)
end
def link_request_params
params.permit(:transactionId, :accountAction)
end
def switch_params
params.permit(:abhaAddress, :scope, :loginHint)
end
def verify_switch_params
params.permit(:transactionId, :abhaAddress)
end
def policy_check
authorize params, policy_class: PatientProfilesControllerPolicy
end
end
end
endSecurity Considerations
Authentication & Authorization
- X-Token Validation: All endpoints require valid X-Token header
- Existing Policy: Use existing
PatientProfilesControllerPolicy - No Additional Authorization: Follow existing pattern (no patient record validation)
Error Handling Pattern
Stateless Error Handling:
- All errors returned immediately via API response
- Use
context.messagefor error messages - Use
context.fail!for failures - No database error tracking or persistence
API Security
- Request Logging: All calls logged to
abdm_requeststable - Token Validation: X-Token validated by ABDM API directly
- No Rate Limiting: Follow existing implementation (no additional rate limiting)
API URL Patterns (From Postman Collection)
Based on the collection analysis, the ABDM PHR Profile API URLs follow this pattern:
Base URL: {abdm_phr_host}/profile/
Update Profile: POST /profile/updateProfile
Update Email: POST /profile/updateEmail/sendOtp, /profile/updateEmail/verifyOtp
Update Mobile: POST /profile/updateMobile/sendOtp, /profile/updateMobile/verifyOtp
Update Password: POST /profile/updatePassword
Link ABHA: POST /profile/linkAbha/sendOtp, /profile/linkAbha/verifyOtp, /profile/linkAbha/linkRequest
Link Aadhaar: POST /profile/linkAadhaar/sendOtp, /profile/linkAadhaar/verifyOtp, /profile/linkAadhaar/linkRequest
Switch Profile: POST /profile/switch, /profile/verifySwitch
Logout: POST /logoutTesting Strategy
Request Specs
# spec/requests/abdm/patient_profiles_spec.rb
RSpec.describe 'Abdm::PatientProfiles', type: :request do
describe 'POST /api/v3/abdm/abha/patients/update_profile' do
it 'updates profile with valid data'
it 'returns error for invalid X-Token'
it 'validates required fields'
end
describe 'Email Update Flow' do
it 'sends OTP to new email'
it 'verifies OTP and updates email'
it 'handles invalid OTP gracefully'
end
describe 'Mobile Update Flow' do
it 'sends OTP to new mobile'
it 'verifies OTP and updates mobile'
it 'handles invalid mobile format'
end
describe 'Password Update' do
it 'updates password with valid current password'
it 'rejects invalid current password'
end
describe 'ABHA Linking Flow' do
it 'sends ABHA OTP successfully'
it 'verifies ABHA OTP and creates link request'
it 'completes linking process'
end
describe 'Profile Switching' do
it 'switches to different profile'
it 'verifies switch with transaction ID'
end
describe 'Logout' do
it 'logs out user successfully'
it 'handles logout errors gracefully'
end
endError Handling
Standard Error Response Format
Following existing ABDM pattern - errors returned directly from API:
{
"success": false,
"message": "The OTP provided is invalid or expired",
"records": null
}Common Error Scenarios
- Invalid X-Token: Token validation failed
- Invalid OTP: OTP verification failed
- Invalid Input: Field validation errors
- API Errors: ABDM service errors passed through directly
Implementation Summary
Files to Create: 14 Service Classes + 8 Interactor Classes = 22 New Files
Files to Modify: 2 Existing Files
PatientProfilesController(add 13 new actions)routes.rb(add 13 new routes)
Key Architectural Decisions
- Stateless Design: No database persistence, following existing ABDM pattern
- Existing Service Pattern: Use HTTParty with AbdmRequest logging
- X-Token Authentication: All APIs require X-Token header like existing profile APIs
- Interactor Pattern: Use BaseInteractor with context passing and fail! pattern
- Error Handling: Simple context.fail! with message, no complex error objects
- API URLs: Use
abdm_phr_hostcredential like existing profile services
Excluded from Implementation
- ❌ PHR Certificate API
- ❌ Refresh Token API
- ❌ Database migrations/models
- ❌ Token persistence
- ❌ Session management
- ❌ Transaction tracking tables