ARTIFACTORY: How to Audit User Logins and Authentication Methods (SAML, LDAP, and Internal)

Products
Frog_Artifactory
Content Type
User_Guide
AuthorFullName__c
Ashritha C Rao
articleNumber
000007148
FirstPublishedDate
2026-09-02T11:11:01Z
lastModifiedDate
2026-09-02

ARTIFACTORY: How to Audit User Logins and Authentication Methods (SAML, LDAP, and Internal)

This KB article provides guidance for auditing authentication events in JFrog Platform (Artifactory/Access) to identify the authentication method used for each login, compare first-time and subsequent logins, and trace authentication realm transitions when the same username authenticates through different providers. This applies to JFrog Access/Artifactory HA deployments using SAML SSO, LDAP, and/or internal (local) user authentication.


Summary
Question
Answer
Can access-security-audit.log alone show the auth method for every login?
Partially. The realm field appears on user create (C|USR) and some user update (U|USR) events not on every login (A|LGN) for existing users.
Official way to determine SAML vs LDAP vs internal for an existing user?
Correlate the trace ID across log files (zgrep "<trace-id>" *.log in Access service logs, including rotated archives).
Is trace ID correlation supported?
Yes. This is the recommended and supported approach for Splunk, reporting, and manual investigation.
Is there a single audit field for “auth source” on all logins?
No current roadmap item for a dedicated field; auth method is inferred from correlated request/outbound logs.
Should SAML/LDAP debug loggers stay enabled permanently?
No. Use them only for short validation; rely on standard logs for ongoing audit.


Key log files and locations
On each Access node, logs are typically under: $JFROG_HOME/artifactory/var/log/access/logs/(or the equivalent path in your support bundle: .../access/logs/ and .../access/logs/archived/)
Log file
Role in auth auditing
access-security-audit.log
Security audit: login (A|LGN), user create/update/delete (C/U/D|USR), tokens (TKN). Realm appears in JSON on creation and on some updates.
access-request.log
Inbound HTTP/gRPC to Access. SAML: POST /access/api/ui/saml/login/<idp>. LDAP/internal UI: GET /access/api/v2/authentication/login, POST /api/v1/access/auth/login.
access-request-out.log
Outbound calls. LDAP: LDAP_USER_SEARCH, SEARCH_LDAP_GROUPS_PAGED. Absent for pure SAML (no directory bind on that trace).
frontend-request.log
UI paths such as POST /api/v1/auth/saml/loginResponse/<idp> (SAML).
artifactory-access.log
[ACCEPTED LOGIN] for the authenticated client (often a different trace ID than the initial SAML handshake—see below).
access-ldap.log
Optional DEBUG lines for LDAP group sync (only when debug is enabled): Synced N user '<username>' ldap groups.
router-request.log
Router-level view of the same paths (useful when Access request logs are split across nodes).
Log entry shape (pipe-delimited):
timestamp|traceId|clientIp|actor|...|actionType|entityType|{json}
For security audit lines, look for:
  • A|LGN — login accepted (audit)
  • C|USR / U|USR / D|USR — user created / updated / deleted
  • C|TKN — session or API token created


How to identify the authentication method
Use this decision flow after you have a login timestamp or an A|LGN line from access-security-audit.log.


Step 1: Capture the trace ID
From the login audit line:
2026-07-21T03:43:19.092Z|54f40b98b825ef8d7755371e774d7aeb|0.0.0.0|<username>|UNKNOWN|UNKNOWN|A|LGN|{"username": "<username>", "remoteAddress": "0.0.0.0"}
Trace ID: 54f40b98b825ef8d7755371e774d7aeb
Search all Access-related logs (include archives):

Shell

cd /path/to/access/logs
zgrep "54f40b98b825ef8d7755371e774d7aeb" *.log */*.log 2>/dev/null


Step 2: Apply fingerprints
Auth method
Strong indicators on the same trace ID
SAML
POST /access/api/ui/saml/login/<provider> in access-request.log
SAML (UI)
POST /api/v1/auth/saml/loginResponse/<provider> in frontend-request.log
LDAP
LDAP_USER_SEARCH and usually SEARCH_LDAP_GROUPS_PAGED in access-request-out.log
LDAP (password UI)
POST /api/v1/access/auth/login and/or GET /access/api/v2/authentication/login in access-request.log / frontend-request.log
Internal (local user)
A|LGN on trace and no SAML path and no LDAP_USER_SEARCH on same trace; often C|TKN "type":"session"
Realm in audit (first create / some updates)
"realm":"saml", "realm":"ldap", or "realm":"internal" in C|USR or U|USR JSON
Important:

Important: [ACCEPTED LOGIN] in artifactory-access.log does not state SAML vs LDAP. It confirms session establishment for a client IP and username. For SAML, the handshake trace (e.g. 2ac6636e463167324677106642dfda78) often differs from the later [ACCEPTED LOGIN] trace issued when the UI loads as the named user (e.g. df2d6d179eb6b47b06ea8599fd8bea65). Always tie auth method to the trace that contains SAML or LDAP outbound evidence, not only the final [ACCEPTED LOGIN] line.


Quick reference grep patterns

Shell

# LDAP outbound activity (any login using LDAP bind)
zgrep "LDAP_USER_SEARCH" access-request-out*.log

# SAML UI login to Access
zgrep "/access/api/ui/saml/login" access-request*.log

# Realm changes and provisioning
grep -E '"realm"|realm' access-security-audit*.log


First login vs subsequent logins
First-time user (provisioned on first successful auth)
When a user does not exist in Access before authentication, provisioning generates C|USR (create user) with realm and typical flags:
LDAP — first login (user created):
2026-07-10T09:34:01.861Z|9132029e4f09162feb17726ca42bf4ba|UNKNOWN|UNKNOWN|anonymous|<username>|C|USR|{"added":{"customData.updatable_profile":"false","customData.disabled_password":"true","groups.readers":"UserGroupImpl(name=readers, realm=internal)","realm":"ldap","status":"enabled","username":"<username>"}}
SAML — first login (user created:
2026-08-04T06:26:21.285Z|2ac6636e463167324677106642dfda78|UNKNOWN|UNKNOWN|anonymous|<username>|C|USR|{"added":{"customData.updatable_profile":"false","customData.disabled_password":"true","groups.readers":"UserGroupImpl(name=readers, realm=internal)","realm":"saml","status":"enabled","username":"<username>"}}
(On first SAML login you would see C|USR with "realm":"saml" and commonly "customData.disabled_password":"true".)
Internal — first login (admin-created or self-registration per policy):
Expect "realm":"internal" in C|USR. Internal users typically do not have disabled_password set for SSO-only flows unless configured.
Typical first-login sequence (all methods):
  1. Auth-specific traffic (SAML POST or LDAP outbound or password login API only).
  2. C|USR with "realm": "<saml|ldap|internal>" (when auto-provisioning applies).
  3. A|LGN with username and remoteAddress.
  4. C|TKN session token(s).
  5. [ACCEPTED LOGIN] in artifactory-access.log (possibly under a follow-on trace ID for SAML).


Subsequent logins (user already exists)
For existing users, A|LGN does not include realms. The user update line may be empty:
2026-07-28T15:32:23.883Z|a119971afed229747c06688ff42047c2|UNKNOWN|UNKNOWN|anonymous|<username>|U|USR|{}
You must use trace ID correlation to classify the login:
SAML → existing user (example trace 54f40b98b825ef8d7755371e774d7aeb):
access-request.log:...|POST|/access/api/ui/saml/login/march|200|...
access-security-audit.log:...|A|LGN|{"username": "<username>", "remoteAddress": "0.0.0.0"}
LDAP → existing user (example trace 47d09904e0cbcc653d0bd5b0f202e82a):
access-request-out.log:...|ou=users/(uid={0})/<username>|LDAP_USER_SEARCH|200|...
access-request-out.log:...|SEARCH_LDAP_GROUPS_PAGED|200|...
(No SAML path on that trace.)
LDAP → existing user with password UI (production example, trace 50de87ef5ff3bce1460bb0ee803b31b4):
access-security-audit.log:...|A|LGN|{"username": "<username>", "remoteAddress": "x.x.x.x"}
access-request.log / frontend-request.log: POST /api/v1/access/auth/login
access-request-out.log: LDAP_USER_SEARCH, SEARCH_LDAP_GROUPS_PAGED
Internal → existing user:
Same password API as LDAP UI login (POST /api/v1/access/auth/login), but no matching LDAP_USER_SEARCH / SEARCH_LDAP_GROUPS_PAGED on that trace. The user's stored realm remains internal unless an admin or a conflicting login changes it (see below).


Same username, different auth methods (realm transition)
Artifactory/Access associates a single user record with one realm at a time. If the same username first logs in via SAML and later via LDAP (or the reverse), the first login after a method switch can produce an explicit realm update in access-security-audit.log.
Example: an account previously SAML, first LDAP login after switch (trace 2ba1584d780e81981d6d5b97ae3a4132):
2026-08-04T07:18:57.753Z|2ba1584d780e81981d6d5b97ae3a4132|UNKNOWN|UNKNOWN|anonymous|<username>|U|USR|{"updated":{"realm":{"oldEntry":"saml","newEntry":"ldap"}}}
2026-08-04T07:18:57.774Z|2ba1584d780e81981d6d5b97ae3a4132|x.x.x.x|<username>|UNKNOWN|UNKNOWN|A|LGN|{"username": "<username>", "remoteAddress": "x.x.x.x"}
On the same trace ID, LDAP evidence appears:
access-request-out.log:...|LDAP_USER_SEARCH|200|...
access-request-out.log:...|SEARCH_LDAP_GROUPS_PAGED|200|...
access-request.log: GET /access/api/v2/authentication/login
access-request.log / frontend-request.log: POST /api/v1/access/auth/login
Later LDAP logins (same user, realm already ldap) may show only A|LGN plus LDAP outbound lines—without another realm update:
2026-08-04T09:25:39.507Z|50de87ef5ff3bce1460bb0ee803b31b4|...|A|LGN|{"username": "<username>", ...}
Audit interpretation:
Event
What to record
U|USR with realm.oldEntry/realm.newEntry
SAML login traces
/access/api/ui/saml/login/... → proves this session used SAML, even if the realm was not rewritten.
LDAP login traces
LDAP_USER_SEARCH → proves this session used LDAP directory authentication.


End-to-end examples by method
SAML login flow (existing user)
  1. access-security-audit.log: A|LGN (trace T1).
  2. access-request.log: POST /access/api/ui/saml/login/azure (same T1).
  3. frontend-request.log: POST /api/v1/auth/saml/loginResponse/azure(often linked via trace parent / T1).
  4. access-security-audit.log: C|TKN session for user.
  5. artifactory-access.log: [ACCEPTED LOGIN] for <service-account> (T1), then later [ACCEPTED LOGIN] for the human user under T2 (separate trace).
Example production correlation:
access-request.log:2026-08-04T06:26:21.301Z|2ac6636e463167324677106642dfda78|...|POST|/access/api/ui/saml/login/azure|200|...
artifactory-access.log:2026-08-04T06:26:21.322Z [2ac6636e463167324677106642dfda78] [ACCEPTED LOGIN]
for client : <service-account> / x.x.x.x [token]
artifactory-access.log:2026-08-04T06:26:23.714Z [df2d6d179eb6b47b06ea8599fd8bea65] [ACCEPTED LOGIN]
for client : <username> / x.x.x.x [token]


LDAP login flow (existing user)
  1. access-request-out.log: LDAP_USER_SEARCH then SEARCH_LDAP_GROUPS_PAGED (trace T).
  2. access-security-audit.log: A|LGN (same T).
  3. access-request.log / frontend-request.log: password login API (/api/v1/access/auth/login) when using UI or CLI password flow.
  4. artifactory-access.log: [ACCEPTED LOGIN] (may use a child trace after token creation).
  5. access-ldap.log (debug only): Synced N user '<username>' ldap groups.
Example:
access-request-out.log:2026-07-21T03:25:19.876Z|47d09904e0cbcc653d0bd5b0f202e82a|...|LDAP_USER_SEARCH|200|...
access-request-out.log:2026-07-21T03:25:19.916Z|47d09904e0cbcc653d0bd5b0f202e82a|...|SEARCH_LDAP_GROUPS_PAGED|200|...


Internal user login flow
  1. access-security-audit.log: A|LGN (start here; copy trace ID).
  2. Exclude SAML: no POST /access/api/ui/saml/login/... on that trace (in access-request.log / router-request.log).
  3. Exclude LDAP: no LDAP_USER_SEARCH / SEARCH_LDAP_GROUPS_PAGED on that trace in access-request-out.log.
  4. Optional supporting evidence: POST /api/v1/access/auth/login, GET /access/api/v2/authentication/login, or gRPC AuthenticationResource/Authenticate—usually on the same trace ID, but often only in access/logs/access-request.log, frontend/logs/frontend-request.log, or router/logs/router-request.log, not in every file under var/log/*.log.
  5. Session: C|TKN with "type":"session" and "username":"<username>".
  6. Historical C|USR or user profile shows "realm":"internal".
Distinguishing internal vs LDAP when the password login line is missing from a casual grep: run zgrep "<trace-id>" on access-request-out.log. LDAP always has outbound directory calls; internal does not.
NOTE: A password login line is the HTTP request log entry when someone signs in with username +
password in the JFrog UI (or CLI)  as opposed to SAML, where the browser is redirected to an IdP.

What it looks like (examples):

1. Frontend: where the UI submits the password: From frontend-request.log: 

2026-07-21T16:10:16.272Z|8792aa71dbda6246bcccd02eb247ecf2|127.0.0.1|anonymous|POST|/api/v1/access/auth/login?_spring_security_remember_me=false|200|844|55|1180.942|Mozilla/5.0 ...

2. Access: backend processes the login: From access-request.log:

2026-08-03T05:27:53.814Z|d2c112616c405c49ada7a0b5e502f0f8|10.10.204.83|anonymous|GET|/access/api/v2/authentication/login|200|828|-1|1363|JFrog Access NodeJS Client/7.226.0

3. Router: same call, JSON format: From router-request.log:

{"RequestMethod":"POST","RequestPath":"/ui/api/v1/access/auth/login?_spring_security_remember_me=false","TraceId":"bc815132a241e8b1f40a2ad80e58efb9","DownstreamStatus":200,...} 
It is not the A|LGN audit line. It is the API call that submits the password.
Start every login investigation from A|LGN in access-security-audit.log and grep the trace ID across access/logs, frontend/logs, router/logs, and access-request-out.log (not only top-level var/log/*.log).
SAML = /access/api/ui/saml/login on the trace; LDAP = LDAP_USER_SEARCH on the trace; internal (including admin) = A|LGN with no SAML path and no LDAP outbound—password auth/login lines are optional confirmation when present.
Realm in audit JSON applies mainly to first provisioning or realm changes; for existing users, trace-based correlation is the supported method to record authentication methods for audit and compliance.