azure-graph throws "Access Token missing or malformed" - javascript

I flawlessly use azure APIs in my Note.js project.
Login:
const MsRest = require('ms-rest-azure');
MsRest.loginWithServicePrincipalSecret(keys.appId, keys.pass, keys.tenantId);
Create resource group:
const { ResourceManagementClient } = require('azure-arm-resource');
const resourceClient = new ResourceManagementClient(credentials, subscriptionId);
resourceClient.resourceGroups.createOrUpdate(groupName, groupParameters);
It works flawlessly, and so do azure-arm-authorization, azure-arm-compute and azure-arm-network modules.
However, I do not manage to use azure-graph API:
const GraphkManagementClient = require('azure-graph');
const client = new GraphkManagementClient(credentials, subscriptionId);
return client.users.get(principalID);
The last line throws an error:
Access Token missing or malformed

Active Directory Graph service is tenant based and has a different token audience compared to other Azure services like resource manger, compute etc. where all these has subscription based token audience. so, the token that is acquired will work for other Azure Services but not for Graph and thus you received the respective token error. Please refer to https://github.com/Azure/azure-sdk-for-node/tree/master/lib/services/graphManagement#how-to-use to understand the implementation of Graph with node.js

Related

What's the equivalent of auth.getIdTokenClient() on Android?

I need to query a token from a GCP function, for that, I want to do what the js function GoogleAuth<JSONClient>.getIdTokenClient(targetAudience) does but on Android.
Right now I'm using this code to generate an auth token:
GoogleCredentials
.fromStream(
app.assets.open("my_config_file.json")
)
.createScoped(
listOf(
"https://www.googleapis.com/auth/cloud-platform"
)
)
But the token generated is a ya29.c., wherewith the getIdToken I get a valid token.
How can I get a valid token as getIdToken on my Android app?
For Java based applications you have the google-auth-library-java. Looking in the docs for this library you have the IdTokenCredentials.Builder and IdTokenCredentials classes.
Also you have an example use case:
String credPath = "/path/to/svc_account.json";
String targetAudience = "https://example.com";
// For Application Default Credentials (as ServiceAccountCredentials)
// export GOOGLE_APPLICATION_CREDENTIALS=/path/to/svc.json
GoogleCredentials adcCreds = GoogleCredentials.getApplicationDefault();
if (!adcCreds instanceof IdTokenProvider) {
// handle error message
}
IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder()
.setIdTokenProvider(adcCreds)
.setTargetAudience(targetAudience).build();
// Use the IdTokenCredential in an authorized transport
GenericUrl genericUrl = new GenericUrl("https://example.com");
HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
HttpTransport transport = new NetHttpTransport();
HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
HttpResponse response = request.execute();
// Print the token, expiration and the audience
System.out.println(tokenCredential.getIdToken().getTokenValue());
System.out.println(tokenCredential.getIdToken().getJsonWebSignature().getPayload().getAudienceAsList());
System.out.println(tokenCredential.getIdToken().getJsonWebSignature().getPayload().getExpirationTimeSeconds());
Documentation:
Github repository
Using OAuth 2.0 with the Google API Client Library for Java

Azure Blob Storage 403 Authentication Failed Due To Authorization Header

Problem
I have uploaded a set of images (blobs) to a private Azure Blob Storage account, but when I try to access them, I am faced with the following error.
GET https://<account-name>.blob.core.windows.net/<container-name>/<blob-name> 403 (Server failed
to authenticate the request. Make sure the value of Authorization header is formed correctly
including the signature.)
I don't have any problems uploading this data as this is done through the server-side using a Django app. I wish to be able to successfully retrieve this uploaded blob data using client-side JavaScript.
Background
I have thoroughly read through and implemented the steps from the Microsoft Azure documentation for authorizing access to my private account via the use of Shared Keys. This includes everything from constructing my signature string to hashing this data using the HMAC SHA-256 algorithm, as detailed in the link above.
I am running everything on Docker containers except for the client-side Vue-based interface which is attempting to invoke the Get Blob API endpoint, as you will see below.
Minimum Reproducible Example
The code that raises this error is as follows:
// Add imports
const crypto = require('crypto');
const axios = require('axios');
// Set Azure blob storage data
const account = "<azure-blob-storage-private-account-name>"
const version = "2020-04-08"
const blob = "<blob-name>"
const container = "<container-name>"
const blob_uri = `https://${account}.blob.core.windows.net/${container}/${blob}`;
const today = new Date().toGMTString();
// Construct signature string
const CanonicalisedHeaders = `x-ms-date:${today}\nx-ms-version:${version}\n`;
const CanonicalisedResource = `/${account}/${container}/${blob}`;
const StringToSign = `GET\n\n\n\n\n\n\n\n\n\n\n\n` + CanonicalisedHeaders + CanonicalisedResource;
// Hash string using HMAC Sha-256 and encode to base64
const key = "<shared-access-key-in-base64>";
const utf8encoded = Buffer.from(key, 'base64').toString('utf8');
const signature = crypto.createHmac('sha256', utf8encoded).update(StringToSign).digest("base64");
// Construct the headers and invoke the API call
const blob_config = {
headers: {
"Authorization": `SharedKey ${account}:${signature}`,
"x-ms-date": today,
"x-ms-version": version
}
}
await axios.get(blob_uri, blob_config)
.then((data) => console.log(data))
.catch((error) => console.log(error.message));
What I have tried
I have tried the following, but none of them have helped me resolve the issue at hand.
Updated CORS settings to avoid CORS-related 403 Forbidden Access issues.
Regenerated my key and connection strings.
Checked the DateTime settings on my local machine and on my Docker containers to ensure they are on the correct GMT time.
Checked that my signature string's components (canonicalized headers, resources, etc.) are constructed according to the rules defined here.
Read through similar StackOverflow and Azure forum posts in search of a solution.
Please try by changing the following lines of code:
const utf8encoded = Buffer.from(key, 'base64').toString('utf8');
const signature = crypto.createHmac('sha256', utf8encoded).update(StringToSign).digest("base64");
to
const keyBuffer = Buffer.from(key, 'base64');
const signature = crypto.createHmac('sha256', keyBuffer).update(StringToSign).digest("base64");
I don't think you need to convert the key buffer to a UTF8 encoded string.
Few other things:
Considering you're using it in the browser, there's a massive security risk as you're exposing your storage keys to your users.
Is there a reason you're using REST API directly instead of using Azure Storage Blob SDK?
In browser-based environments, you should be using Shared Access Signature based authorization instead of Shared Access Key based authorization.

What scope is required to access the list of AzureAD groups via MS Graph API?

I am trying to add a feature to a MS Graph API app written in Node.JS. The app was built from a Microsoft template. The app needs to fetch a list of Azure AD groups whose names begin with a prefix. I am having trouble getting it to work.
I copied this working code:
const client = graph.Client.init({...});
const events = await client
.api('/me/events')
.select('subject,organizer,start,end')
.orderby('createdDateTime DESC')
.get();
...and changed the api query according to the documentation I found. I have commented out the filter just to try to get the basic query to work.
const client = graph.Client.init({...});
const events = await client
.api('/groups')
.select('id,displayName')
// .filter("startsWith(displayName,'Prefix')")
.get();
But the query returns this error:
GraphError {
statusCode: 403,
code: 'Authorization_RequestDenied',
message: 'Insufficient privileges to complete the operation.',
requestId: 'f5330dbd-c7a9-4126-a227-10a656a11083',
date: 2020-07-10T22:25:24.000Z,
body: '{"code":"Authorization_RequestDenied"...}'
}
I'm not 100% certain the URI is correct. I would expect a 404 if the URI was wrong, but that may be expecting too much. Perhaps this /group API does not correlate to Azure AD groups.
OTOH, do I need to add a new scope to request access to the group information? The current scope list used by the application looks like this:
OAUTH_SCOPES='profile offline_access user.read calendars.read'
I have not found a reference for graph queries and scopes for Azure AD. I tried adding 'groups.read' to the list of scopes, but that did not work.
I would appreciate not only an answer, but link to documentation as well. I have to add a few other features from Azure AD.
The least to most privileged Permission for querying Groups is Group.Read.All as per your linked document. https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0&tabs=javascript#permissions

Web3/Metamask: Error: Contract has not been deployed to detected network (network/artifact mismatch) on Kovan network

I try to deploy an instance of a contract that is already live on the Kovan network to interact with it with web3 and metamask.
So first thing first, I set metamask as my current provider then I deploy an instance of the contract like this:
deployContract = (contract) => {
contract.deployed().then(function(instance) {
let proxy = instance;
return proxy.ProxyAddress()
}).then(function(result){
this.setState({
address: result,
});
})
}
But then, I get the following error:
Uncaught (in promise) Error: Contract has not been deployed to detected network (network/artifact mismatch)
at eval (webpack:///./~/truffle-contract/contract.js?:429)
at <anonymous>
I found out that it was caused by the network ID of web3 which happen to be wrong.
My web3 network ID is set by Metamask which is supposed to inject web3 with the correct ID. But when I get the network ID I get a totaly different result:
web3.version.getNetwork(function(err,res){console.log(res)})
> 3
Is there a way to manualy set web3's version network? I looked in the documentation and github but there was no usefull insights.
EDIT:
It appears that closing and reopening chrome resolve the ID problem. So now I have both ID set as 42 but the error is still the same as before. Back to square one...
Ok so in the end the problem was caused by the way of importing my contracts. My previous import was done like this:
let contract = require('truffle-contract');
let Factory = contract("../contracts/Factory.json");
While it should actualy be imported this way:
let contract = require('truffle-contract');
let json = require("../contracts/Factory.json");
let Factory = contract(json);
So to sum up, if an error like this happen to you, do those checks first:
-Check contract import.
-Check your web3 provider. console.log(window.web3.currentProvider)
-Check web3 network ID. web3.version.getNetwork(function(err,res{console.log(res)})

Creating a YouTube Service via ASP.NET using a pre-existing Access Token

I've been working on a Website for users to upload videos to a shared YouTube account for later access. After much work I've been able to get an Active Token, and viable Refresh Token.
However, the code to initialize the YouTubeService object looks like this:
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name,
});
I've already got a token, and I want to use mine. I'm using ASP.NET version 3.5, and so I can't do an async call anyways.
Is there any way I can create a YouTubeService object without the async call, and using my own token? Is there a way I can build a credential object without the Authorization Broker?
Alternatively, the application used YouTube API V2 for quite some time, and had a form that took a token, and did a post action against a YouTube URI that was generated alongside the token in API V2. Is there a way I can implement that with V3? Is there a way to use Javascript to upload videos, and possibly an example that I could use in my code?
NOTE: I ended up upgrading my Framework to 4.5 to access the google libraries.
To programatically initialize a UserCredential Object you've got to build a Flow, and TokenResponse. A Flow Requires a Scope (aka the permissions we are seeking for the credentials.
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Auth.OAuth2.Flows;
string[] scopes = new string[] {
YouTubeService.Scope.Youtube,
YouTubeService.Scope.YoutubeUpload
};
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = XXXXXXXXXX, <- Put your own values here
ClientSecret = XXXXXXXXXX <- Put your own values here
},
Scopes = scopes,
DataStore = new FileDataStore("Store")
});
TokenResponse token = new TokenResponse {
AccessToken = lblActiveToken.Text,
RefreshToken = lblRefreshToken.Text
};
UserCredential credential = new UserCredential(flow, Environment.UserName, token);
Hope that helps.
Currently the official Google .NET client library does not work with .NET Framework 3.5. (Note: this is an old question the library hasn't supported .NET 3.5 since 2014. So the statement would have been valid then as well.) That being said you are not going to be able to create a service for the Google .NET client library using an existing access token. Also not possible to create it with an access token using any .NET Framework you would need to create your own implementation of Idatastore and load a refresh token.
Supported Platforms
.NET Framework 4.5 and 4.6
.NET Core (via netstandard1.3 support)
Windows 8 Apps
Windows Phone 8 and 8.1
Portable Class Libraries
That being said you are going to have to code this yourself from the ground up. I have done it and it is doable.
Authentication :
You have stated you have your refresh token already so I won't go into how to create that.
The following is a HTTP POST call
Refresh access token request:
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
Refresh Access token response:
{ "access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ", "token_type" : "Bearer", "expires_in" : 3600 }
An call you make to the YouTube API you can either add the access token as the authorization bearer token or you can just take it on to the end of any request
https://www.googleapis.com/youtube/v3/search?access_token={token here}
I have a full post on all of the calls to the auth server Google 3 legged Oauth2 flow. I just use normal webRequets for all my calls.
// Create a request for the URL.
WebRequest request = WebRequest.Create("http://www.contoso.com/default.html");
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
Console.WriteLine (((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
// Clean up the streams and the response.
reader.Close();
response.Close();
Upgrade .NET 4+
If you can upgrade to the newest version of .NET using the library will be much easier. This is from Googles official documentation Web Applications ASP.NET. I have some additional sample code on my github account which shoes how to use the Google Drive API. Google dotnet samples YouTube data v3.
using System;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v2;
using Google.Apis.Util.Store;
namespace Google.Apis.Sample.MVC4
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "PUT_CLIENT_ID_HERE",
ClientSecret = "PUT_CLIENT_SECRET_HERE"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
}
Top tip YouTube doesn't support service accounts your going to have to stick with Oauth2. As long as you have authenticated your code once it should continue to work.

Categories