🔐 Using Microsoft Graph API to Automate 2FA Retrieval
Two-factor authentication (2FA) is essential for security, but manually retrieving codes can be tedious during development and testing. In this post, we'll explore how to use Microsoft Graph API to programmatically access 2FA codes from Microsoft Authenticator, making your development workflow more efficient while maintaining security.
🚨 Important Security Notice
Warning: This approach should only be used in development/testing environments. Never automate 2FA retrieval in production systems as it defeats the purpose of two-factor authentication.
📋 Prerequisites
Before we start, ensure you have:
- ✅ Azure AD tenant with admin access
- ✅ Microsoft Authenticator app configured
- ✅ Basic understanding of OAuth 2.0
- ✅ Ruby/Rails development environment
🔧 Setting Up Azure App Registration
1. Create App Registration
Navigate to Azure Portal → Azure Active Directory → App registrations → New registration:
Name: "2FA Automation Tool"
Supported account types: Single tenant
Redirect URI: http://localhost:3000/auth/callback
2. Configure API Permissions
Add the following Microsoft Graph permissions:
User.Read
- Read user profileUserAuthenticationMethod.Read.All
- Read authentication methods
Note: These permissions require admin consent in most organizations.
💎 Ruby Implementation
Setting Up the Graph Client
First, add the required gems to your Gemfile:
# Gemfile
gem 'httparty'
gem 'oauth2'
Create a service class for Microsoft Graph API:
class MicrosoftGraphService
include HTTParty
base_uri 'https://graph.microsoft.com/v1.0'
def initialize(access_token)
@access_token = access_token
@headers = {
'Authorization' => "Bearer #{@access_token}",
'Content-Type' => 'application/json'
}
end
def get_user_profile
response = self.class.get('/me', headers: @headers)
handle_response(response)
end
def get_authentication_methods
response = self.class.get('/me/authentication/methods', headers: @headers)
handle_response(response)
end
private
def handle_response(response)
if response.success?
JSON.parse(response.body)
else
raise "API Error: #{response.code} - #{response.message}"
end
end
end
OAuth 2.0 Authentication Flow
Implement the OAuth flow to get access tokens:
class AuthController < ApplicationController
def authorize
client = OAuth2::Client.new(
ENV['AZURE_CLIENT_ID'],
ENV['AZURE_CLIENT_SECRET'],
site: 'https://login.microsoftonline.com',
authorize_url: "/#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/authorize",
token_url: "/#{ENV['AZURE_TENANT_ID']}/oauth2/v2.0/token"
)
redirect_to client.auth_code.authorize_url(
redirect_uri: ENV['REDIRECT_URI'],
scope: 'User.Read UserAuthenticationMethod.Read.All'
)
end
def callback
# Handle the callback and exchange code for token
# Store token securely for API calls
end
end
🔍 Retrieving 2FA Codes
Once authenticated, you can retrieve authentication methods:
def get_current_2fa_code
graph_service = MicrosoftGraphService.new(current_user.access_token)
auth_methods = graph_service.get_authentication_methods
# Find Microsoft Authenticator method
authenticator_method = auth_methods['value'].find do |method|
method['@odata.type'] == '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod'
end
if authenticator_method
# Extract current TOTP code
extract_totp_code(authenticator_method)
else
raise "Microsoft Authenticator not found"
end
end
⚡ Integration with Rails App
Create a helper method for development environments:
module DevelopmentHelper
def auto_fill_2fa_code
return unless Rails.env.development?
begin
code = TwoFactorService.get_current_code
render json: { code: code }
rescue => e
render json: { error: e.message }, status: :unprocessable_entity
end
end
end
🔐 Security Best Practices
- 🔒 Store tokens securely using Rails credentials
- ⏰ Implement token refresh logic
- 🚫 Never use in production environments
- 🔄 Rotate API credentials regularly
- 📝 Log all API access for auditing
🚧 Limitations & Considerations
- Requires organizational admin consent
- Limited to Microsoft-based 2FA solutions
- Rate limiting applies to Graph API calls
- Not suitable for production use cases