in Microsoft.Azure.Cosmos/src/Authorization/AuthorizationHelper.cs [196:310]
public static void ParseAuthorizationToken(
string authorizationTokenString,
out ReadOnlyMemory<char> typeOutput,
out ReadOnlyMemory<char> versionOutput,
out ReadOnlyMemory<char> tokenOutput)
{
typeOutput = default;
versionOutput = default;
tokenOutput = default;
if (string.IsNullOrEmpty(authorizationTokenString))
{
DefaultTrace.TraceError("Auth token missing");
throw new UnauthorizedException(RMResources.MissingAuthHeader);
}
int authorizationTokenLength = authorizationTokenString.Length;
authorizationTokenString = HttpUtility.UrlDecode(authorizationTokenString);
// Format of the token being deciphered is
// type=<master/resource/system>&ver=<version>&sig=<base64encodedstring>
// Step 1. split the tokens into type/ver/token.
// when parsing for the last token, I use , as a separator to skip any redundant authorization headers
ReadOnlyMemory<char> authorizationToken = authorizationTokenString.AsMemory();
int typeSeparatorPosition = authorizationToken.Span.IndexOf('&');
if (typeSeparatorPosition == -1)
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
ReadOnlyMemory<char> authType = authorizationToken.Slice(0, typeSeparatorPosition);
authorizationToken = authorizationToken.Slice(typeSeparatorPosition + 1, authorizationToken.Length - typeSeparatorPosition - 1);
int versionSepartorPosition = authorizationToken.Span.IndexOf('&');
if (versionSepartorPosition == -1)
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
ReadOnlyMemory<char> version = authorizationToken.Slice(0, versionSepartorPosition);
authorizationToken = authorizationToken.Slice(versionSepartorPosition + 1, authorizationToken.Length - versionSepartorPosition - 1);
ReadOnlyMemory<char> token = authorizationToken;
int tokenSeparatorPosition = authorizationToken.Span.IndexOf(',');
if (tokenSeparatorPosition != -1)
{
token = authorizationToken.Slice(0, tokenSeparatorPosition);
}
// Step 2. For each token, split to get the right half of '='
// Additionally check for the left half to be the expected scheme type
int typeKeyValueSepartorPosition = authType.Span.IndexOf('=');
if (typeKeyValueSepartorPosition == -1
|| !authType.Span.Slice(0, typeKeyValueSepartorPosition).SequenceEqual(Constants.Properties.AuthSchemaType.AsSpan())
|| !authType.Span.Slice(0, typeKeyValueSepartorPosition).ToString().Equals(Constants.Properties.AuthSchemaType, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
ReadOnlyMemory<char> authTypeValue = authType.Slice(typeKeyValueSepartorPosition + 1);
if (MemoryExtensions.Equals(authTypeValue.Span, Constants.Properties.AadToken.AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (authorizationTokenLength > AuthorizationHelper.MaxAadAuthorizationHeaderSize)
{
DefaultTrace.TraceError($"Token of type [{authTypeValue.Span.ToString()}] was of size [{authorizationTokenLength}] while the max allowed size is [{AuthorizationHelper.MaxAadAuthorizationHeaderSize}].");
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat, SubStatusCodes.InvalidAuthHeaderFormat);
}
}
else if (MemoryExtensions.Equals(authTypeValue.Span, Constants.Properties.ResourceToken.AsSpan(), StringComparison.OrdinalIgnoreCase))
{
if (authorizationTokenLength > AuthorizationHelper.MaxResourceTokenAuthorizationHeaderSize)
{
DefaultTrace.TraceError($"Token of type [{authTypeValue.Span.ToString()}] was of size [{authorizationTokenLength}] while the max allowed size is [{AuthorizationHelper.MaxResourceTokenAuthorizationHeaderSize}].");
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat, SubStatusCodes.InvalidAuthHeaderFormat);
}
}
else if (authorizationTokenLength > AuthorizationHelper.MaxAuthorizationHeaderSize)
{
DefaultTrace.TraceError($"Token of type [{authTypeValue.Span.ToString()}] was of size [{authorizationTokenLength}] while the max allowed size is [{AuthorizationHelper.MaxAuthorizationHeaderSize}].");
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat, SubStatusCodes.InvalidAuthHeaderFormat);
}
int versionKeyValueSeparatorPosition = version.Span.IndexOf('=');
if (versionKeyValueSeparatorPosition == -1
|| !version.Span.Slice(0, versionKeyValueSeparatorPosition).SequenceEqual(Constants.Properties.AuthVersion.AsSpan())
|| !version.Slice(0, versionKeyValueSeparatorPosition).ToString().Equals(Constants.Properties.AuthVersion, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
ReadOnlyMemory<char> versionValue = version.Slice(versionKeyValueSeparatorPosition + 1);
int tokenKeyValueSeparatorPosition = token.Span.IndexOf('=');
if (tokenKeyValueSeparatorPosition == -1
|| !token.Slice(0, tokenKeyValueSeparatorPosition).Span.SequenceEqual(Constants.Properties.AuthSignature.AsSpan())
|| !token.Slice(0, tokenKeyValueSeparatorPosition).ToString().Equals(Constants.Properties.AuthSignature, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
ReadOnlyMemory<char> tokenValue = token.Slice(tokenKeyValueSeparatorPosition + 1);
if (authTypeValue.IsEmpty ||
versionValue.IsEmpty ||
tokenValue.IsEmpty)
{
throw new UnauthorizedException(RMResources.InvalidAuthHeaderFormat);
}
typeOutput = authTypeValue;
versionOutput = versionValue;
tokenOutput = tokenValue;
}