Skip to content

Commit 25ce0fd

Browse files
committed
Fix blob SAS when using version 2026-04-06
1 parent bdd3cfe commit 25ce0fd

3 files changed

Lines changed: 112 additions & 1 deletion

File tree

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
55
## Upcoming Release
66

7+
Blob:
8+
9+
- Added support for delegation SAS version 2026-04-06.
10+
711
## 2026.06 Version 3.36.0
812

913
General:

src/blob/authentication/BlobSASAuthenticator.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,14 @@ export default class BlobSASAuthenticator implements IAuthenticator {
478478
const signedStartsOn = this.decodeIfExist(req.getQuery("skt"));
479479
const signedExpiresOn = this.decodeIfExist(req.getQuery("ske"));
480480
const signedVersion = this.decodeIfExist(req.getQuery("skv"));
481+
const delegatedUserTenantId = this.decodeIfExist(req.getQuery("skdutid"));
481482
const signedService = this.decodeIfExist(req.getQuery("sks"));
483+
const signedRequestHeaders = this.decodeIfExist(
484+
req.getQuery("srh")
485+
);
486+
const signedRequestQueryParameters = this.decodeIfExist(
487+
req.getQuery("srq")
488+
);
482489

483490
if (!identifier && (!permissions || !expiryTime)) {
484491
this.logger.warn(
@@ -520,8 +527,11 @@ export default class BlobSASAuthenticator implements IAuthenticator {
520527
signedTenantId,
521528
signedService,
522529
signedVersion,
530+
delegatedUserTenantId,
523531
signedStartsOn,
524-
signedExpiresOn
532+
signedExpiresOn,
533+
signedRequestHeaders,
534+
signedRequestQueryParameters
525535
};
526536

527537
return blobSASValues;

src/blob/authentication/IBlobSASSignatureValues.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ export interface IBlobSASSignatureValues {
106106
*/
107107
cacheControl?: string;
108108

109+
/**
110+
* Optional. Custom Request Headers to include in the SAS
111+
*
112+
* @type {string}
113+
* @memberof IBlobSASSignatureValues
114+
*/
115+
signedRequestHeaders?: string;
116+
117+
/**
118+
* Optional. Custom Request Query Parameters to include in the SAS
119+
*
120+
* @type {string}
121+
* @memberof IBlobSASSignatureValues
122+
*/
123+
signedRequestQueryParameters?: string;
124+
109125
/**
110126
* Optional. The content-disposition header for the SAS.
111127
*
@@ -292,6 +308,14 @@ export function generateBlobSASSignatureWithUDK(
292308
accountName: string,
293309
udkValue: Buffer
294310
): [string, string] {
311+
if (blobSASSignatureValues.version >= "2026-04-06") {
312+
return generateBlobSASBlobSASSignatureWithUDK20260406(
313+
blobSASSignatureValues,
314+
resource,
315+
accountName,
316+
udkValue
317+
);
318+
}
295319
if (blobSASSignatureValues.version >= "2025-07-05") {
296320
return generateBlobSASBlobSASSignatureWithUDK20250705(
297321
blobSASSignatureValues,
@@ -803,6 +827,79 @@ function generateBlobSASBlobSASSignatureWithUDK20250705(
803827
return [signature, stringToSign];
804828
}
805829

830+
function generateBlobSASBlobSASSignatureWithUDK20260406(
831+
blobSASSignatureValues: IBlobSASSignatureValues,
832+
resource: BlobSASResourceType,
833+
accountName: string,
834+
userDelegationKeyValue: Buffer
835+
): [string, string] {
836+
if (
837+
!blobSASSignatureValues.identifier &&
838+
!blobSASSignatureValues.permissions &&
839+
!blobSASSignatureValues.expiryTime
840+
) {
841+
throw new RangeError(
842+
// tslint:disable-next-line:max-line-length
843+
"generateBlobSASSignature(): Must provide 'permissions' and 'expiryTime' for Blob SAS generation when 'identifier' is not provided."
844+
);
845+
}
846+
847+
const verifiedPermissions = blobSASSignatureValues.permissions;
848+
849+
// Signature is generated on the un-url-encoded values.
850+
const stringToSign = [
851+
verifiedPermissions ? verifiedPermissions : "",
852+
blobSASSignatureValues.startTime === undefined
853+
? ""
854+
: typeof blobSASSignatureValues.startTime === "string"
855+
? blobSASSignatureValues.startTime
856+
: truncatedISO8061Date(blobSASSignatureValues.startTime, false),
857+
blobSASSignatureValues.expiryTime === undefined
858+
? ""
859+
: typeof blobSASSignatureValues.expiryTime === "string"
860+
? blobSASSignatureValues.expiryTime
861+
: truncatedISO8061Date(blobSASSignatureValues.expiryTime, false),
862+
getCanonicalName(
863+
accountName,
864+
blobSASSignatureValues.containerName,
865+
resource === BlobSASResourceType.Blob
866+
? blobSASSignatureValues.blobName
867+
: ""
868+
),
869+
blobSASSignatureValues.signedObjectId,
870+
blobSASSignatureValues.signedTenantId,
871+
blobSASSignatureValues.signedStartsOn,
872+
blobSASSignatureValues.signedExpiresOn,
873+
blobSASSignatureValues.signedService,
874+
blobSASSignatureValues.signedVersion,
875+
undefined, // blobSASSignatureValues.preauthorizedAgentObjectId,
876+
undefined, // agentObjectId
877+
undefined, // blobSASSignatureValues.correlationId,
878+
blobSASSignatureValues.delegatedUserTenantId, // SignedKeyDelegatedUserTenantId, will be added in a future release.
879+
blobSASSignatureValues.delegatedUserObjectId, // SignedDelegatedUserObjectId, will be added in future release.
880+
blobSASSignatureValues.ipRange === undefined
881+
? ""
882+
: typeof blobSASSignatureValues.ipRange === "string"
883+
? blobSASSignatureValues.ipRange
884+
: ipRangeToString(blobSASSignatureValues.ipRange),
885+
blobSASSignatureValues.protocol ? blobSASSignatureValues.protocol : "",
886+
blobSASSignatureValues.version,
887+
resource,
888+
undefined, // blob version timestamp,
889+
blobSASSignatureValues.encryptionScope,
890+
blobSASSignatureValues.signedRequestHeaders,
891+
blobSASSignatureValues.signedRequestQueryParameters,
892+
blobSASSignatureValues.cacheControl,
893+
blobSASSignatureValues.contentDisposition,
894+
blobSASSignatureValues.contentEncoding,
895+
blobSASSignatureValues.contentLanguage,
896+
blobSASSignatureValues.contentType
897+
].join("\n");
898+
899+
const signature = computeHMACSHA256(stringToSign, userDelegationKeyValue);
900+
return [signature, stringToSign];
901+
}
902+
806903
function getCanonicalName(
807904
accountName: string,
808905
containerName: string,

0 commit comments

Comments
 (0)