Wednesday, August 31, 2022

Project vs. collection

Some time ago, I've discussed the identity of the account that TFS agent jobs are using to connect back to TFS via the distributedTask PowerShell context variable, or the equivalent System.AccessToken release variable. Back at the time, I've concluded that the identity behind that token was "Build Service (CollectionName)". Well, just today I've encountered a case where a release was running under a different identity - "ProjectName Build Service (CollectionName)". Both coexist.

I can not, for now, tell what determines which of those two artificial identities is used for releases in each particular project. To make matters more difficult, the "get identity" endpoint would fail with the token. Other endpoints for reading project specific data were failing, too. 

Turns out, there is another way to tell the identity behind the token. TFS tokens are JSON Web Tokens (JWTs), a JWT consists of three dot character separated parts - header, claims, and signature. The former two are Base64-encoded JSON (in UTF-8), with the twist that the flavor of Base64 is the filename safe kind. Inside the claims there is a field called nameid that is a GUID, corresponding to the Id field in the tbl_Identity table of the Tfs_Configuration database. So if you run the following PowerShell:

$Endpoint = $distributedTaskContext.GetEndpoint("SystemVssConnection")
$Token = $Endpoint.Authorization.P
arameters.AccessToken
$c = $Token.Split('.')[1]
$c = $c.Replace('-', '+').Replace('_', '/')
$c = $c.PadRight($c.Length + 4 - $c.Length%4, '=')
$c = [Convert]::FromBase64String($c)
$c = 
[System.Text.Encoding]::UTF8.GetString($c)
$Claims = $c | ConvertFrom-Json
Write-Host $Claims.nameid

You will have a nameid that can be used in the following SQL against the TFS database:

select *
from Tfs_Configuration.dbo.tbl_Identity
where Id='paste-nameid-here'

By the way, the failing endpoints stopped failing once I've added the "(Project) Build Service" to the project team.

There is also a built-in token parser in .NET (JwtSecurityTokenHandler), but it's more trouble in this particular context than it's worth. Also, one can dump the token contents and parse it interactively with https://jwt.io . To the best of my knowledge, there is no out of the box parser for filename safe Base64 in .NET.

No comments:

Post a Comment