debug(microsoft-shared): log token scopes/audience/user on IMAP access failure
Net::IMAP raises NoResponseError ('AUTHENTICATE failed.') without exposing
the underlying Microsoft challenge response. Adding the JWT payload's scp,
aud, and user-email claims to the failure log lets us distinguish the
common causes:
- granted_scopes missing IMAP.AccessAsUser.All -> Microsoft silently
downgraded the scope at consent time (Azure app config / consent screen
issue)
- aud not matching outlook.office.com -> token issued for wrong resource
(rare; would mean OAuth flow misconfigured)
- scopes correct -> tenant-level policy (OAuth2ClientProfileEnabled,
Conditional Access blocking IMAP, etc.)
Read-only inspection of our own token; no change to the auth flow itself.
This commit is contained in:
@@ -19,10 +19,13 @@ class Microsoft::Shared::ImapAccessVerifier
|
||||
imap = Net::IMAP.new(IMAP_HOST, port: IMAP_PORT, ssl: true)
|
||||
imap.authenticate('XOAUTH2', upn, access_token)
|
||||
imap.capability
|
||||
Rails.logger.info("Microsoft Shared IMAP access verified: upn=#{upn}")
|
||||
Rails.logger.info("Microsoft Shared IMAP access verified: upn=#{upn} oauth_user=#{token_user_email.inspect} scopes=#{token_scopes.inspect}")
|
||||
true
|
||||
rescue Net::IMAP::NoResponseError, Net::IMAP::BadResponseError, Net::IMAP::ResponseError => e
|
||||
Rails.logger.warn("Microsoft Shared IMAP access denied: upn=#{upn} #{e.class}: #{e.message}")
|
||||
Rails.logger.warn(
|
||||
"Microsoft Shared IMAP access denied: upn=#{upn} oauth_user=#{token_user_email.inspect} " \
|
||||
"scopes=#{token_scopes.inspect} aud=#{token_audience.inspect} #{e.class}: #{e.message}"
|
||||
)
|
||||
false
|
||||
rescue StandardError => e
|
||||
Rails.logger.warn("Microsoft Shared IMAP access check raised: upn=#{upn} #{e.class}: #{e.message}")
|
||||
@@ -39,4 +42,29 @@ class Microsoft::Shared::ImapAccessVerifier
|
||||
# ignore
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Decode the access token's JWT payload (without verifying signature) so we can
|
||||
# log what scopes Microsoft actually granted, the audience the token is for,
|
||||
# and the user it was issued to. Helps distinguish 'wrong scope', 'wrong
|
||||
# audience', 'right scope but tenant policy blocks IMAP' failures from each
|
||||
# other.
|
||||
def token_payload
|
||||
@token_payload ||= JWT.decode(access_token, nil, false).first
|
||||
rescue StandardError
|
||||
{}
|
||||
end
|
||||
|
||||
def token_scopes
|
||||
token_payload['scp']
|
||||
end
|
||||
|
||||
def token_audience
|
||||
token_payload['aud']
|
||||
end
|
||||
|
||||
def token_user_email
|
||||
token_payload['email'] || token_payload['preferred_username'] || token_payload['upn']
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user