PHR Login Implementation Plan
Executive Summary
This document outlines the implementation plan for PHR (Personal Health Record) login functionality following the existing ABDM module conventions. The implementation follows a stateless architecture without database session storage, maintaining consistency with the current ABDM implementation patterns. The system supports multiple login methods including Mobile, Email, ABHA Address, and ABHA Number with both Aadhaar and ABHA OTP verification.
Key Architecture Decision
No Database Session Storage: Based on analysis of the existing ABDM system, we will NOT create database tables for session management. All session state (transaction IDs, tokens) will be managed by the frontend client, following the current stateless API pattern.
High-Level PHR Login Flow
Login Methods Supported
- Mobile Number Login - OTP-based authentication via registered mobile
- Email Login - OTP-based authentication via registered email
- ABHA Address Login - Using ABHA address with:
- Mobile OTP
- Email OTP
- Password authentication
- ABHA Number Login - Using ABHA number with:
- Aadhaar OTP
- ABHA OTP
- Aadhaar Direct Login - Using Aadhaar number with OTP
Authentication Flow Steps
- Request OTP → Generate OTP for chosen authentication method, return
txnIdto frontend - Verify OTP → Validate OTP using
txnId, receive and return T-token to frontend - Verify User → Use T-token to verify user, return X-token and R-token to frontend
- Session Management → Frontend stores and manages all tokens and session state
Implementation Architecture
Stateless Design Principles
- No Server-Side Sessions: All session data managed by frontend
- Token Pass-Through: Backend acts as a secure proxy to ABDM APIs
- Request Logging Only: Use existing
AbdmRequestmodel for audit logging - Direct Response Forwarding: Return ABDM API responses directly to frontend
Service Layer (Stateless Implementation)
1. Mobile Login - Request OTP
# parts/abdm/services/abdm/phr/login/mobile/request_otp.rb
module Abdm
module Phr
module Login
module Mobile
class RequestOtp
def self.perform(mobile, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_mobile = Abdm::Abha::EncryptPhrData.perform(mobile)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/mobileOrEmailOtp/init"
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = {
value: encrypted_mobile,
purpose: 'CM_ACCESS',
authMode: 'MOBILE_OTP',
requester: {
type: 'PHR',
id: 'IN0410000183'
}
}
begin
result = HTTParty.post(url, body: body.to_json, headers: headers)
rescue StandardError => e
error = e.to_s
end
# Log Request
abdm_request = AbdmRequest.create!(
url: url,
payload: body,
requested_by_id: user_id,
requested_by_type: 'User'
)
# Update with response
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
end
end
end
end
end2. Mobile Login - Verify OTP
# parts/abdm/services/abdm/phr/login/mobile/verify_otp.rb
module Abdm
module Phr
module Login
module Mobile
class VerifyOtp
def self.perform(otp, txn_id, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_otp = Abdm::Abha::EncryptPhrData.perform(otp)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/mobileOrEmailOtp/preVerify"
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = {
transactionId: txn_id,
authCode: encrypted_otp
}
begin
result = HTTParty.post(url, body: body.to_json, headers: headers)
rescue StandardError => e
error = e.to_s
end
# Log Request
abdm_request = AbdmRequest.create!(
url: url,
payload: body,
requested_by_id: user_id,
requested_by_type: 'User'
)
# Update with response
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
end
end
end
end
end3. Email Login Services
# parts/abdm/services/abdm/phr/login/email/request_otp.rb
module Abdm
module Phr
module Login
module Email
class RequestOtp
def self.perform(email, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_email = Abdm::Abha::EncryptPhrData.perform(email)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/mobileOrEmailOtp/init"
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = {
value: encrypted_email,
purpose: 'CM_ACCESS',
authMode: 'EMAIL_OTP',
requester: {
type: 'PHR',
id: 'IN0410000183'
}
}
# Similar request handling as Mobile
# ... (same pattern as mobile)
result
end
end
end
end
end
end4. ABHA Address Login Services
# parts/abdm/services/abdm/phr/login/abha_address/request_mobile_otp.rb
module Abdm
module Phr
module Login
module AbhaAddress
class RequestMobileOtp
def self.perform(abha_address, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_abha = Abdm::Abha::EncryptPhrData.perform(abha_address)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/mobileOrEmailOtp/init"
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = {
value: encrypted_abha,
purpose: 'CM_ACCESS',
authMode: 'MOBILE_OTP',
requester: {
type: 'PHR',
id: 'IN0410000183'
}
}
# Standard request handling...
result
end
end
# parts/abdm/services/abdm/phr/login/abha_address/verify_password.rb
class VerifyPassword
def self.perform(abha_address, password, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_abha = Abdm::Abha::EncryptPhrData.perform(abha_address)
encrypted_password = Abdm::Abha::EncryptPhrData.perform(password)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/abhaAddress/authPassword"
body = {
abhaAddress: encrypted_abha,
password: encrypted_password
}
# Standard request handling...
result
end
end
end
end
end
end5. ABHA Number Login Services
# parts/abdm/services/abdm/phr/login/abha_number/request_aadhaar_otp.rb
module Abdm
module Phr
module Login
module AbhaNumber
class RequestAadhaarOtp
def self.perform(abha_number, user_id)
token = Abdm::FetchGatewayTokenV3.perform
encrypted_number = Abdm::Abha::EncryptPhrData.perform(abha_number)
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/aadhaar/generateOtp"
body = {
aadhaar: encrypted_number,
purpose: 'CM_ACCESS'
}
# Standard request handling...
result
end
end
# parts/abdm/services/abdm/phr/login/abha_number/request_abha_otp.rb
class RequestAbhaOtp
def self.perform(abha_number, user_id)
# Similar to above but different endpoint
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/abha/generateOtp"
# ... implementation
end
end
end
end
end
end6. User Verification Service (Final Step)
# parts/abdm/services/abdm/phr/login/verify_user.rb
module Abdm
module Phr
module Login
class VerifyUser
def self.perform(transaction_id, t_token, user_id)
token = Abdm::FetchGatewayTokenV3.perform
url = "#{Rails.application.credentials.abdm_phr_host}/v3/phr/login/userAuthorizedToken"
headers = {
'Content-Type' => 'application/json',
'Authorization' => "Bearer #{token}",
'X-Token' => "Bearer #{t_token}",
'REQUEST-ID' => SecureRandom.uuid,
'TIMESTAMP' => Time.now.utc.iso8601(3)
}
body = {
transactionId: transaction_id,
patientId: nil,
purpose: 'LINK'
}
begin
result = HTTParty.post(url, body: body.to_json, headers: headers)
rescue StandardError => e
error = e.to_s
end
# Log Request
abdm_request = AbdmRequest.create!(
url: url,
payload: body,
requested_by_id: user_id,
requested_by_type: 'User'
)
# Update with response
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
end
end
end
endInteractors (Business Logic - Stateless)
1. Mobile Login Send OTP Interactor
# parts/abdm/interactors/abdm/phr/login/mobile/send_otp.rb
module Abdm
module Phr
module Login
module Mobile
class SendOtp < BaseInteractor
def call
require_context(:mobile)
validate_mobile
result = Abdm::Phr::Login::Mobile::RequestOtp.perform(
context.mobile,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
context.message = 'OTP sent successfully to mobile'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to send OTP' })
end
end
private
def validate_mobile
fail_with_error!('mobile', 'is required') if context.mobile.blank?
fail_with_error!('mobile', 'is invalid') unless valid_mobile?(context.mobile)
end
def valid_mobile?(mobile)
mobile.match?(/\A[6-9]\d{9}\z/)
end
end
end
end
end
end2. Mobile Login Verify OTP Interactor
# parts/abdm/interactors/abdm/phr/login/mobile/verify_otp.rb
module Abdm
module Phr
module Login
module Mobile
class VerifyOtp < BaseInteractor
def call
require_context(:otp)
require_context(:transaction_id)
result = Abdm::Phr::Login::Mobile::VerifyOtp.perform(
context.otp,
context.transaction_id,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
context.message = 'OTP verified successfully'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to verify OTP' })
end
end
end
end
end
end
end3. Email Login Interactors
# parts/abdm/interactors/abdm/phr/login/email/send_otp.rb
module Abdm
module Phr
module Login
module Email
class SendOtp < BaseInteractor
def call
require_context(:email)
validate_email
result = Abdm::Phr::Login::Email::RequestOtp.perform(
context.email,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
context.message = 'OTP sent successfully to email'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to send OTP' })
end
end
private
def validate_email
fail_with_error!('email', 'is required') if context.email.blank?
fail_with_error!('email', 'is invalid') unless valid_email?(context.email)
end
def valid_email?(email)
email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
end
end
end
end
end
end
# parts/abdm/interactors/abdm/phr/login/email/verify_otp.rb
# Similar structure to Mobile::VerifyOtp4. ABHA Address Login Interactors
# parts/abdm/interactors/abdm/phr/login/abha_address/send_mobile_otp.rb
module Abdm
module Phr
module Login
module AbhaAddress
class SendMobileOtp < BaseInteractor
def call
require_context(:abha_address)
validate_abha_address
result = Abdm::Phr::Login::AbhaAddress::RequestMobileOtp.perform(
context.abha_address,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
context.message = 'OTP sent successfully'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to send OTP' })
end
end
private
def validate_abha_address
fail_with_error!('abha_address', 'is required') if context.abha_address.blank?
fail_with_error!('abha_address', 'is invalid') unless valid_abha_address?(context.abha_address)
end
def valid_abha_address?(address)
address.match?(/^[a-zA-Z0-9._]+@[a-zA-Z]+$/)
end
end
end
end
end
end
# parts/abdm/interactors/abdm/phr/login/abha_address/send_email_otp.rb
# parts/abdm/interactors/abdm/phr/login/abha_address/verify_mobile_otp.rb
# parts/abdm/interactors/abdm/phr/login/abha_address/verify_email_otp.rb
# parts/abdm/interactors/abdm/phr/login/abha_address/verify_password.rb5. ABHA Number Login Interactors
# parts/abdm/interactors/abdm/phr/login/abha_number/send_aadhaar_otp.rb
module Abdm
module Phr
module Login
module AbhaNumber
class SendAadhaarOtp < BaseInteractor
def call
require_context(:abha_number)
validate_abha_number
result = Abdm::Phr::Login::AbhaNumber::RequestAadhaarOtp.perform(
context.abha_number,
context.current_user&.id
)
if result&.success?
context.records = result.parsed_response
context.message = 'Aadhaar OTP sent successfully'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to send OTP' })
end
end
private
def validate_abha_number
fail_with_error!('abha_number', 'is required') if context.abha_number.blank?
fail_with_error!('abha_number', 'is invalid') unless valid_abha_number?(context.abha_number)
end
def valid_abha_number?(number)
number.match?(/^\d{2}-\d{4}-\d{4}-\d{4}$/)
end
end
end
end
end
end
# parts/abdm/interactors/abdm/phr/login/abha_number/send_abha_otp.rb
# parts/abdm/interactors/abdm/phr/login/abha_number/verify_aadhaar_otp.rb
# parts/abdm/interactors/abdm/phr/login/abha_number/verify_abha_otp.rb6. Aadhaar Login Interactors
# parts/abdm/interactors/abdm/phr/login/aadhaar/send_otp.rb
# parts/abdm/interactors/abdm/phr/login/aadhaar/verify_otp.rb7. Verify User Interactor (Common)
# parts/abdm/interactors/abdm/phr/login/verify_user.rb
module Abdm
module Phr
module Login
class VerifyUser < BaseInteractor
def call
require_context(:transaction_id)
require_context(:t_token)
result = Abdm::Phr::Login::VerifyUser.perform(
context.transaction_id,
context.t_token,
context.current_user&.id
)
if result&.success?
# Optionally update clinic patient if linked
update_clinic_patient(result.parsed_response) if context.clinic_patient_id
context.records = result.parsed_response
context.message = 'User verified and logged in successfully'
else
fail_with_errors!(result&.parsed_response || { error: 'Failed to verify user' })
end
end
private
def update_clinic_patient(response_data)
clinic_patient = ClinicPatient.find_by(id: context.clinic_patient_id)
return unless clinic_patient
clinic_patient.update!(
phr_linked: true,
phr_linked_at: Time.current,
abha_address: response_data['phrAddress'],
abha_number: response_data['ABHANumber']
)
end
end
end
end
endControllers
PHR Login Controller (Stateless - Complete)
# parts/abdm/controllers/abdm/phr/login_controller.rb
module Abdm
module Phr
class LoginController < ApiController
before_action :policy_check
# Mobile Login Endpoints
# POST /abdm/phr/login/mobile/send_otp
def mobile_send_otp
result = Login::Mobile::SendOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/mobile/verify_otp
def mobile_verify_otp
result = Login::Mobile::VerifyOtp.call(params)
render_result(result)
end
# Email Login Endpoints
# POST /abdm/phr/login/email/send_otp
def email_send_otp
result = Login::Email::SendOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/email/verify_otp
def email_verify_otp
result = Login::Email::VerifyOtp.call(params)
render_result(result)
end
# ABHA Address Login Endpoints
# POST /abdm/phr/login/abha_address/send_mobile_otp
def abha_address_send_mobile_otp
result = Login::AbhaAddress::SendMobileOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_address/verify_mobile_otp
def abha_address_verify_mobile_otp
result = Login::AbhaAddress::VerifyMobileOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_address/send_email_otp
def abha_address_send_email_otp
result = Login::AbhaAddress::SendEmailOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_address/verify_email_otp
def abha_address_verify_email_otp
result = Login::AbhaAddress::VerifyEmailOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_address/verify_password
def abha_address_verify_password
result = Login::AbhaAddress::VerifyPassword.call(params)
render_result(result)
end
# ABHA Number Login Endpoints
# POST /abdm/phr/login/abha_number/send_aadhaar_otp
def abha_number_send_aadhaar_otp
result = Login::AbhaNumber::SendAadhaarOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_number/verify_aadhaar_otp
def abha_number_verify_aadhaar_otp
result = Login::AbhaNumber::VerifyAadhaarOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_number/send_abha_otp
def abha_number_send_abha_otp
result = Login::AbhaNumber::SendAbhaOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/abha_number/verify_abha_otp
def abha_number_verify_abha_otp
result = Login::AbhaNumber::VerifyAbhaOtp.call(params)
render_result(result)
end
# Aadhaar Direct Login Endpoints
# POST /abdm/phr/login/aadhaar/send_otp
def aadhaar_send_otp
result = Login::Aadhaar::SendOtp.call(params)
render_result(result)
end
# POST /abdm/phr/login/aadhaar/verify_otp
def aadhaar_verify_otp
result = Login::Aadhaar::VerifyOtp.call(params)
render_result(result)
end
# Common Endpoint
# POST /abdm/phr/login/verify_user
def verify_user
result = Login::VerifyUser.call(params)
render_result(result)
end
private
def policy_check
authorize params, policy_class: PhrLoginControllerPolicy
end
end
end
endRoutes Configuration
# parts/abdm/routes.rb (Addition to existing routes)
Rails.application.routes.draw do
defaults format: :json do
namespace :abdm do
namespace :phr do
namespace :login do
# Mobile login
namespace :mobile do
post :send_otp
post :verify_otp
end
# Email login
namespace :email do
post :send_otp
post :verify_otp
end
# ABHA Address login
namespace :abha_address do
post :send_mobile_otp
post :verify_mobile_otp
post :send_email_otp
post :verify_email_otp
post :verify_password
end
# ABHA Number login
namespace :abha_number do
post :send_aadhaar_otp
post :verify_aadhaar_otp
post :send_abha_otp
post :verify_abha_otp
end
# Aadhaar login
namespace :aadhaar do
post :send_otp
post :verify_otp
end
# Common endpoints
post :verify_user
end
end
end
end
endAuthorization Policy
# parts/abdm/policies/abdm/phr/login_controller_policy.rb
module Abdm
module Phr
class LoginControllerPolicy < BasePolicy
# Mobile login
def mobile_send_otp?
true # Public endpoint
end
def mobile_verify_otp?
true # Public endpoint
end
# Email login
def email_send_otp?
true # Public endpoint
end
def email_verify_otp?
true # Public endpoint
end
# ABHA Address login
def abha_address_send_mobile_otp?
true # Public endpoint
end
def abha_address_verify_mobile_otp?
true # Public endpoint
end
def abha_address_send_email_otp?
true # Public endpoint
end
def abha_address_verify_email_otp?
true # Public endpoint
end
def abha_address_verify_password?
true # Public endpoint
end
# ABHA Number login
def abha_number_send_aadhaar_otp?
true # Public endpoint
end
def abha_number_verify_aadhaar_otp?
true # Public endpoint
end
def abha_number_send_abha_otp?
true # Public endpoint
end
def abha_number_verify_abha_otp?
true # Public endpoint
end
# Aadhaar login
def aadhaar_send_otp?
true # Public endpoint
end
def aadhaar_verify_otp?
true # Public endpoint
end
# Common
def verify_user?
true # Public endpoint
end
end
end
endAPI Request/Response Examples (Stateless)
1. Mobile Login - Send OTP
Request:
POST /abdm/phr/login/mobile/send_otp
{
"mobile": "9876543210",
"clinic_patient_id": 123 // Optional
}Response:
{
"success": true,
"message": "OTP sent successfully to mobile",
"data": {
"transactionId": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
}
}2. Mobile Login - Verify OTP
Request:
POST /abdm/phr/login/mobile/verify_otp
{
"otp": "123456",
"transaction_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
}Response:
{
"success": true,
"message": "OTP verified successfully",
"data": {
"authToken": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"abhaAddress": ["john.doe@abdm", "johndoe@sbx"]
}
}3. Verify User
Request:
POST /abdm/phr/login/verify_user
{
"transaction_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
"t_token": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"clinic_patient_id": 123 // Optional
}Response:
{
"success": true,
"message": "User verified and logged in successfully",
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 1800,
"phrAddress": "john.doe@abdm",
"ABHANumber": "91-1234-5678-9012"
}
}Error Handling
Standard Error Response Format
{
"success": false,
"errors": {
"code": "PHR_INVALID_OTP",
"message": "Invalid OTP provided"
}
}Common Error Scenarios
- Invalid OTP format or expired OTP
- Invalid transaction ID
- Token expired or invalid
- ABDM API connectivity issues
- Invalid mobile number format
Key Considerations (Stateless Architecture)
1. Frontend Responsibilities
- Store and manage transaction IDs between API calls
- Manage T-tokens and final access tokens
- Handle token expiry and refresh logic
- Maintain user session state
2. Security Considerations
- All sensitive data encrypted using
EncryptPhrDataservice - No session data stored in backend database
- API request/response logging via
AbdmRequestmodel - Rate limiting should be implemented at API gateway level
3. Token Flow
- Transaction ID: Returned from send OTP, used in verify OTP
- T-Token: Returned from verify OTP, used in verify user
- Access Token: Returned from verify user, used for PHR API calls
- Refresh Token: Used to refresh expired access tokens
4. Multiple ABHA Addresses
- After OTP verification, response may contain multiple ABHA addresses
- Frontend must allow user to select one address
- Selected address passed to verify user endpoint
Testing Strategy
1. Unit Tests
# spec/services/abdm/phr/login/mobile/request_otp_spec.rb
# spec/interactors/abdm/phr/login/mobile/send_otp_spec.rb2. Integration Tests
# spec/requests/abdm/phr/login_flow_spec.rb3. Mock ABDM Responses
- Mock successful OTP generation responses
- Mock OTP verification responses with T-token
- Mock user verification responses with access tokens
Configuration Requirements
Add to Rails credentials:
abdm_phr_host: "https://dev.abdm.gov.in/api"Implementation Summary
Stateless Architecture Benefits
- Simplicity: No database session management complexity
- Scalability: Stateless backend scales horizontally
- Consistency: Follows existing ABDM module patterns
- Security: No sensitive session data stored in database
Files to Create
1. Controller (1 file)
parts/abdm/controllers/abdm/phr/login_controller.rb
2. Interactors (Business Logic - 20 files)
- Mobile:
mobile/send_otp.rb,mobile/verify_otp.rb - Email:
email/send_otp.rb,email/verify_otp.rb - ABHA Address:
abha_address/send_mobile_otp.rbabha_address/verify_mobile_otp.rbabha_address/send_email_otp.rbabha_address/verify_email_otp.rbabha_address/verify_password.rb
- ABHA Number:
abha_number/send_aadhaar_otp.rbabha_number/verify_aadhaar_otp.rbabha_number/send_abha_otp.rbabha_number/verify_abha_otp.rb
- Aadhaar:
aadhaar/send_otp.rb,aadhaar/verify_otp.rb - Common:
verify_user.rb
3. Services (API Communication - 20 files)
Same structure as interactors, handling actual ABDM API calls
4. Policy (1 file)
parts/abdm/policies/abdm/phr/login_controller_policy.rb
5. Routes (Update 1 file)
- Update existing
parts/abdm/routes.rb
Total: ~42 files to implement
Key Implementation Notes
- No database migrations required
- Frontend manages all session state
- Backend acts as secure proxy to ABDM APIs
- Uses existing
AbdmRequestmodel for logging - Uses existing
EncryptPhrDataservice for encryption
This implementation maintains consistency with the existing ABDM codebase while providing PHR login functionality in a clean, stateless manner.