As per this doc, I constructed SAS key.
Get the OAuth Token from Azure Ad. Generate user delegation key using OAuth. Generate SAS Token using user delegation key
However, I get the error
Access Denied. (message: 'Access to persistent storage path 'https://storage.blob.core.windows.net/container/dir/test.csv' was denied .
I do have rwdl permission. what am I doing wrong here ?
val xml = "" \\ User delegation xml
val signedOid = (xml \ "SignedOid").text
val signedTid = (xml \ "SignedTid").text
val signedKeyStart = (xml \ "SignedStart").text
val signedKeyExpiry = (xml \ "SignedExpiry").text
val signedService = (xml \ "SignedService").text
val signedVersion = (xml \ "SignedVersion").text
val keyValue = (xml \ "Value").text
// 2. Define SAS parameters
val resource = "c" // 'c' for container-level access
val permissions = "rwdl"
val sasStart = OffsetDateTime.now().minusMinutes(5).format(DateTimeFormatter.ISO_INSTANT)
val sasExpiry = OffsetDateTime.now().plusHours(1).format(DateTimeFormatter.ISO_INSTANT)
val protocol = "https"
val apiVersion = signedVersion
// 3. Construct the string to sign per Azure’s spec
val canonicalResource = s"/blob/storage/container"
val stringToSign = Seq(
permissions, // 1. signedPermissions
sasStart, // 2. signedStart
sasExpiry, // 3. signedExpiry
canonicalResource, // 4. canonicalizedResource
signedOid, // 5. signedObjectId
signedTid, // 6. signedTenantId
signedKeyStart, // 7. signedKeyStart
signedKeyExpiry, // 8. signedKeyExpiry
signedService, // 9. signedService
signedVersion, // 10. signedVersion
"", // 11. signedAuthorizedUserObjectId
"", // 12. signedUnauthorizedUserObjectId
"", // 13. signedCorrelationId
"", // 14. signedIP
protocol, // 15. signedProtocol (e.g., "https")
"", // 16. signedEncryptionScope
"", // 17. rscc - cache control
"", // 18. rscd - content disposition
"", // 19. rsce - content encoding
"", // 20. rscl - content language
"", // 21. rsct - content type
resource // 22. signedResource ("c" for container)
).mkString("\n")
// 4. Compute HMAC-SHA256 signature
val keyBytes = Base64.getDecoder.decode(keyValue)
val mac = Mac.getInstance("HmacSHA256")
mac.init(new SecretKeySpec(keyBytes, "HmacSHA256"))
val signatureBytes = mac.doFinal(stringToSign.getBytes("UTF-8"))
val signatureEncoded = URLEncoder.encode(Base64.getEncoder.encodeToString(signatureBytes), "UTF-8")
// 5. Build SAS token string
val sasToken = Seq(
s"sp=$permissions",
s"st=$sasStart",
s"se=$sasExpiry",
s"skoid=$signedOid",
s"sktid=$signedTid",
s"skt=$signedKeyStart",
s"ske=$signedKeyExpiry",
s"sks=$signedService",
s"skv=$signedVersion",
s"spr=$protocol",
s"sv=$apiVersion",
s"sr=$resource",
s"sig=$signatureEncoded"
).mkString("&")