Mobile app performing an HTTP Post to Azure Function using Bearer Token and Function Key returns Unauthorized - azure

I'm using a mobile app and am receiving an Unauthorized response when attempting to post to an Azure Function and providing a function key.
Error:
StatusCode: 401, ReasonPhrase: 'Unauthorized'
Code:
let postToAsync (baseAddress:string) (resource:string) (payload:Object) =
async {
let tokenSource = new CancellationTokenSource(TimeSpan(0,0,30));
let token = tokenSource.Token;
try
let tokens = resource.Split("?code=")
let functionKey = tokens.[1]
use client = httpClient baseAddress
client.DefaultRequestHeaders.Add("x-functions-key", functionKey)
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue("application/json"))
let json = JsonConvert.SerializeObject(payload)
let content = new StringContent(json, Encoding.UTF8, "application/json")
let! response = client.PostAsync(resource.Replace($"?code={functionKey}",""), content, token) |> Async.AwaitTask
Debug.WriteLine $"\n\n{baseAddress}{resource}\nSuccess: {response.IsSuccessStatusCode}\n\n"
return response
with ex -> ...
} |> Async.StartAsTask
Note:
My Azure Function's AuthorizationLevel is set to Function.
I can call the function successfully when I publish it manually from Visual Studio.
However, when I deploy the function using Pulumi, I receive an Unauthorized response. I believe this is because Pulumi constrains me to add access policies for each Function App.
Versioning:
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
oauth2/v2.0:
I think the following link provides a clue to why I'm observing the issue. However, I still don't know how to resolve it.
Connectivity
I launched Log Stream and observed that the URL is correct:
Access Control:
Please note that the difference between the Function App that I created without using Pulumi, which lets me post successfully, versus the Function App that was generated using Pulumi, is an Access Policy per Function App with Pulumi.
public static class AccessPolicies
{
public static void Build(string policyName, string functionName, Dictionary<string, CustomResource> registry)
{
var resourceGroup = registry[nameof(ResourceGroup)] as ResourceGroup;
var keyVault = registry[nameof(KeyVault)] as KeyVault;
var functionApp = registry[functionName] as FunctionApp;
var result = new AccessPolicy(policyName, new AccessPolicyArgs {
KeyVaultId = keyVault.Id,
TenantId = TenantId.Value,
ObjectId = functionApp.Identity.Apply(v => v.PrincipalId ?? "11111111-1111-1111-1111-111111111111"),
KeyPermissions = new[] { "Get", },
SecretPermissions = new[] { "Get", },
});
registry.Add($"{policyName}-{functionName}", result);
}
}
}

I tried to reproduce the same in my environment via Postman and got below results:
I have one function app with http function named srifunction like below:
I generated one bearer token with same scope as you like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://management.azure.com/.default
Response:
When I used the above token to call function, I got 401 Unauthorized error same as you like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <token>
If you pass function key in token value, you will still get 401 Unauthorized error like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <function key>
To call function using function key, you need to include key value
in x-functions-key header instead of Bearer token.
When I included the above header, I am able to call the function successfully like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
x-functions-key: <function key>

Related

Swift Auth0 access code results in unauthorized access but nothing in Auth0 logs

I have a nestJS backend protected with Auth0. I am able to successfully access the backend from react-admin by including the access token in the authorization header (Authorization: Bearer ACCESS_TOKEN)
However, I seem to have an issue when trying to access the same backend from a Swift iOS app. I have followed the Auth0 tutorials and am able to confirm successful user login and access to user profile. However, when I try to make a request to the nestJS backend, I receive a 401 Unauthorized error. Interestingly, nothing is recorded in the Auth0 logs.
Link to Auth0 tutorial: https://auth0.com/docs/quickstart/native/ios-swift/04-calling-apis
let path = "\(baseURL)\(endpoint.rawValue)"
guard let url = URL(string: path)
else { preconditionFailure("Bad URL") }
var headers: [String:String] = [:]
headers["Content-Type"] = "application/json"
// if access token is set then set Authorization headers
if (accessToken != nil) {
headers["Authorization"] = "Bearer \(accessToken!)"
print("Bearer \(accessToken!)")
}
var request = URLRequest(url: url)
request.httpMethod = "\(method)"
request.allHTTPHeaderFields = headers
// check if body exists
if (body != nil) {
request.httpBody = body!
}
let dataTask = URLSession.shared.dataTask(with: request) {
(data, response, error) in
guard error == nil
else { completion(.failure(.serverError)); return }
do {
guard let data = data
else { completion(.failure(.serverError)); return }
guard let object : [[String: AnyObject]] = try JSONSerialization.object(with: data) as? [[String: AnyObject]]
else {
print("Unable to convert from data")
return
}
guard let json = try? JSONSerialization.data(withJSONObject: object, options: .prettyPrinted)
else {
print("Unable to prettify")
return
}
guard let jsonString = String(data: json, encoding: .utf8)
else {
print("Unable to convert to string")
return
}
print("JSON: \(jsonString)")
completion(Result.success(object))
} catch {
completion(Result.failure(.parsingError))
}
}
dataTask.resume()
baseURL is a string that points to my nestJS backend.
endpoint is an enum of endpoints, for example \user
Using Proxyman I am able to confirm that the endpoint is hit with the correct headers. Screenshot below.
Additionally, using postman I am able to successfully login and also make a get request to protected data. Screenshot below.
Any ideas to what might be the cause? Let me know if I should add any additional details.
UPDATE
I decoded the successful (react-admin) and unsuccessful (iOS) JWT tokens and noticed the following differences:
aud in the successful JWT contains an array of audiences that include the API registerd on Auth0 as well as an auth0 endpoint https://xxxxx.us.auth0.com/userinfo
azp is only present in the successful JWT and contains my clientID
aud in the unsuccessful token contains the clientID
scope and permissions is missing from unsuccessful token.
Ps. also posted on Auth0 Community
https://community.auth0.com/t/access-token-when-obtained-from-ios-results-in-401-unauthorized-while-from-react-admin-is-ok/71115
The problem was that audience was set when requesting access tokens from the react-admin, while I did not include this in the swift login implementation.
Decoding the JWT on jwt.io and the following thread lead to this conclusion.
https://community.auth0.com/t/access-token-too-short-jwt-malformed/9169/11?u=kennethphough
Adding the audience in the following code resulted in the correct jwt being returned and successful access to backend.
Auth0
.authentication()
.login(
usernameOrEmail: self.email,
password: self.password,
realm: "Username-Password-Authentication",
audience: "<YOUR_AUDIENCE>", // <- This is what I forgot
scope: "openid profile email"
)

DocuSign.eSign.Client.ApiException {"error":"consent_required"}" / Response Type Not Supported when attempting to grant consent

I tried to receive a token with the code below. Unfortunatelly I get the error:
DocuSign.eSign.Client.ApiException
HResult=0x80131500
Nachricht = Error while requesting server, received a non successful HTTP code with response Body: {"error":"consent_required"}
I tried with set TLS 12 and without. We run it in dev mode with base path https://demo.docusign.net/restapi
and oAuthBasePath =account-d.docusign.com
I tried also to set the consens manually with the URL below. But I receive the error in (Login Window) invalid Authorization: RequestType is not supported.
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=a5ed47d5-xxxx-xxxx-8a19-756da64391de&redirect_uri=https://www.docusign.com
Is the something wrong with my account setting?
byte[] privateKey=DSHelper.ReadFileContent(DSHelper.PrepareFullPrivateKeyFilePath(privateKeyFilename));
var scopes = new List<string>
{
"signature",
"impersonation",
};
var basePath = ApiClient.Production_REST_BasePath;
var oAuthBasePath = OAuth.Production_OAuth_BasePath;
if (!production)
{
basePath = ApiClient.Demo_REST_BasePath;
oAuthBasePath = OAuth.Demo_OAuth_BasePath;
}
var _apiClient = new ApiClient(basePath);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var authToken = _apiClient.RequestJWTUserToken(
clientId,
ImpersonatedUserId,
oAuthBasePath,
privateKey,
1,
scopes);
I found the solution. instead of reponse_type= code I have to use token
What response_type is supported for an integration key depends on how the key is configured. In the Authentication section of the key's configuration panel, Auth Code Grant allows the response type of code, while Implicit Grant allows the response type of token.
DocuSign's authentication docs assume you have "Auth Code Grant" selected, but either is technically acceptable to allow JWT consent.

SignalR and Azure functions with AuthorizationLevel.Function

Trying to protect my azure function with a function key (AuthorizationLevel.Function)
My azure function uses signalR.
If I use AuthorizationLevel.Function on negotiate and my other signalR entry points, how can I pass the function key when the javascript code connects to signalR:
function:
public static SignalRConnectionInfo Negotiate(
[HttpTrigger( AuthorizationLevel.Function, "post" )] HttpRequest req,
[SignalRConnectionInfo( HubName = "myHub")] SignalRConnectionInfo connectionInfo,
ILogger log )
{
return connectionInfo;
}
website:
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://<myfunction>.azurewebsites.net')
.configureLogging(signalR.LogLevel.Information)
.build();
connection.start()
.catch(console.error);
It seems HubConnectionBuilder can access the Headers in c# but not in javascript.
I have read Add headers to #aspnet/signalr Javascript client
but the first suggestion appends the key to the url, and when connecting it would append /negotiate to it resulting in an invalid url with https://host/&code=/negotiate.
If it's not possible, any alternate way to protect my signalR function suggested?
(Maybe bearer token as in https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1)
Thank you
If you take a look at the documentation of the withUrl method, it allows an options object as a second parameter:
function withUrl(url: string, options: IHttpConnectionOptions)
The type of options is IHttpConnectionOptions, which gives some possibilities:
a) you can provide the implementation of IHttpConnectionOptions.accessTokenFactory, which should return a Bearer token (for this to work, you'd need to manually validate the Bearer token in your Azure Function)
b) you can provide your own HttpClient implemenatation, in which you'd modify the Post request by adding the Azure function key, something like below:
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://<myfunction>.azurewebsites.net', {
httpClient: {
post: (url, httpOptions) => {
const headers = {
...httpOptions.headers,
'x-functions-key': YOUR_AZURE_FUNCTION_KEY,
}
return axios.post(url, {}, { headers }).then((response) => {
return (newResponse = {
statusCode: response.status,
statusText: response.statusText,
content: JSON.stringify(response.data),
})
})
},
},
})
.configureLogging(signalR.LogLevel.Information)
.build()
Sources:
https://learn.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-3.1
How to pass Custom Header from React JS client to SignalR hub?
As noted in my answer to the question linked to in this question, you can now do as follows:
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://<myfunction>.azurewebsites.net', {
headers: {'x-functions-key': YOUR_AZURE_FUNCTION_KEY}
})
.build();

Azure Cognitive services - TTS

I got an api keys for Azure Cognitive services, but I can't find any documentation how I am calling this service through postmen. Anybody has experience with this?
Seems you are trying to call Text To Speech service with your keys. There are two steps for that.
1. Need Access Token
You have to get your token like this format:
Request URL: https://YourResourceEndpoint/sts/v1.0/issuetoken
Method: POST
Hearder: Content-Type:application/x-www-form-urlencoded
Ocp-Apim-Subscription-Key:YourKeys
See The Screen shot for clarity:
Code Snippet:
public async Task<string> GetSpeechServiceToken()
{
try
{
string tokenUrl = $"https://YourServiceURL.cognitiveservices.azure.com/sts/v1.0/issuetoken";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Headers.Add("Ocp-Apim-Subscription-Key", "subscriptionKey");
using (var client = new HttpClient())
{
var tokenResponse = await client.SendAsync(tokenRequest);
var token = await tokenResponse.Content.ReadAsStringAsync();
return token;
}
}
catch (Exception ex)
{
ex.Message.ToString();
}
return null;
}
You could have a look on official Docs
2. Get List Of Voices With Token You Have Received Earlier
You can request for Text To Speech voice list Like below:
Request URL: https://centralus.tts.speech.microsoft.com/cognitiveservices/voices/list
Method : GET
Authorization: Bearer Token Paste Your Token Here
See the screen shot for clarity
You could find more details here
Note: In case of your test account You can create here
Update:
I would sent a request and somehow I got an uri or something where I can hear it? is this possible?
Yeah its possible. But in that case you have to use sdk. Here Is the complete sample.

Failed to get AccessToken via authorization code using MSAL 1.1.0-preview in asp.net core

I followed official steps as below to try the scenario "web app calling a Web API in Azure Ad B2C", the only difference is I am using Asp.Net core. I am using AuthorizationCode to get the access token, but it always returns with id token and NULL access token.
Create an Azure AD B2C tenant.
Register a web api.
Register a web app.
Set up policies.
Grant the web app permissions to use the web api.
My code:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
AutomaticChallenge = true,
ClientId = aadB2cSettings.ClientId,
MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
var authCode = context.TokenEndpointRequest.Code;
var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
var cca = new ConfidentialClientApplication(
aadB2cSettings.ClientId,
b2cAuthority,
aadB2cSettings.RedirectUrl,
new ClientCredential(aadB2cSettings.ClientSecret),
new TokenCache(),
null);
try
{
var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
}
catch (Exception ex)
{
throw ex;
}
}
},
Used fiddler to capture the request, it is:
POST
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin
HTTP/1.1
Request Body:
client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F
Return:
{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}
So only id token, no access token. But we should get access token here, right?
Finally found out my failure reason: the request to get AuthorizationCode doesn't contain the target scope. Reflect in code, for OpenIdConnectOption in aspnetcore, the Scope parameter is readonly and its default value is "opened profile".
Scope is readonly in OpenIdConnectOption
So the default authorization code request sent is:
GET
https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=......
HTTP/1.1
Thus, using this authorization code in response to get token, even we set right scope in the token request, we still can't get the access code but only id token, because the provide authorization code is only for "openid profile".
To fix this, we need to add target web api scope into the authorization code as well. Here is the how-to-fix code:
Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.Scope += $" offline_access {myapiscope}";
return Task.FromResult(0);
},
......
}
In AspNet, we don't need to do this because its scope is not readonly as aspnetcore and can be set directly:
new OpenIdConnectAuthenticationOptions
{
......
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}
https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi/issues/4 Microsoft have reproduced the issue and working on fix

Resources