Skip to main content

Microsoft Graph Developer Proxy

· 4 min read
Stephan Hochdörfer

Last month Microsoft introduced a new release of the Microsoft Graph Developer Proxy. The latest release introduces support for monitoring specific processes, a new plugin allowing testing against rate limiting supported APIs and provides a new OData paging guidance.

What made the proxy interesting to me, is the support for mocking responses. Last year, I blogged about a different mechanism I came up with while developing an internal tool that talks to the MS Graph API. I was really curious about how both approaches compare to each other.

By default, Microsoft recommends configuring the proxy as a system proxy which I felt is a bit odd. Ideally, my nodejs application should directly talk to the proxy and the rest of my system is not aware of that proxy. However, that made things a bit more complicated as nodejs does not come with proxy support out-of-the-box. It took me quite a while to figure out how to properly do that.

My application is using the Microsoft Graph client library for JavaScript to communicate with the Graph API. Additionally, we are using the Microsoft Authentication Library to authenticate against the MS Graph API. The detailed steps are covered in this blog post.

Thanks to the middleware architecture of the Graph client library, configuring the proxy usage can be accomplished like this:

import { Middleware } from "@microsoft/microsoft-graph-client";
import { Context } from "@microsoft/microsoft-graph-client";
import {HttpsProxyAgent} from "https-proxy-agent";

export class ProxyMiddleware implements Middleware {
private proxyUrl: string;

private nextMiddleware!: Middleware;

public constructor(proxyUrl: string) {
this.proxyUrl = proxyUrl;
}

public async execute(context: Context): Promise<void> {
if (context.options) {
context.options.agent = new HttpsProxyAgent(this.proxyUrl);
}

return await this.nextMiddleware.execute(context);
}

public setNext(next: Middleware): void {
this.nextMiddleware = next;
}
}

The middleware configures the context.options object with a HttpsProxyAgent for the given proxy url.

I am using the isomorphic-fetch nodejs package to provide the implementation for fetch() for the nodejs server. On its own the isomorphic-fetch implementation does not provide any proxy implementation. This is why I also had to install the https-proxy-agent nodejs package. Since the communication with the MS Graph API is done via HTTPS, it was important to install the https-proxy-agent package and not to use http-proxy-agent package.

But even with using the https-proxy-agent nodejs package, I ran into a problem. The nodejs process complained "unable to verify the first certificate" - the only fix I could find was to set the environment variable NODE_TLS_REJECT_UNAUTHORIZED = 0. Not ideal, but at least I got it working.

Now, let's wire everything together in a test:

import {Client, ClientOptions, MiddlewareFactory} from "@microsoft/microsoft-graph-client";
import {ClientTokenAuthProvider} from "../src/infrastructure/msgraph/clientTokenAuthProvider";
import {ProxyMiddleware} from "./infrastructure/msgraph/proxy-middleware";
import 'isomorphic-fetch';

describe('MS Graph API request', () => {
test('Test Proxy middleware', async () => {
const clientId = '...';
const clientSecret = '...';
const tenantId = '...';

const authProvider = new ClientTokenAuthProvider(clientId, clientSecret, tenantId);
const middlewares = MiddlewareFactory.getDefaultMiddlewareChain(authProvider);
middlewares.unshift(new ProxyMiddleware('http://localhost:8000'));

const clientOptions: ClientOptions = {
middleware: middlewares
};

const client = Client.initWithMiddleware(clientOptions);
const response = await client.api("/users").get();
});
});

The ClientTokenAuthProvider is a custom implementation, that is covered here.

The MiddlewareFactory returns an array of default middlewares provided by the Graph client library, the ProxyMiddleware is added as the first entry in the list which is then passed to the Graph client. Once the client is initialized, requests will automatically be forwarded via the configured proxy.

But what can you do with the Microsoft Graph Developer Proxy?

Well, you could run it just as a proxy like this: mgdp --no-mocks -f 0

Alternatively, you can let the proxy serve mocks for some urls: mgdp --mocks-file ./responses.json -f 0. The responses.json file looks like this:

{
"responses": [
{
"url": "https://graph.microsoft.com/v1.0/users",
"responseBody": {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [
"+1 425 555 0109"
],
"displayName": "Adele Vance",
"givenName": "Adele",
"jobTitle": "Product Marketing Manager",
"mail": "AdeleV@M365x214355.onmicrosoft.com",
"mobilePhone": null,
"officeLocation": "18/2111",
"preferredLanguage": "en-US",
"surname": "Vance",
"userPrincipalName": "AdeleV@M365x214355.onmicrosoft.com",
"id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd"
}
}
]
}

If you want to test the resilience of your application, the -f parameter defines the percentage of requests to respond with failures. Additionally, you can specify the HTTP error codes that should be returned with the -a parameter.

Overall, I really like the proxy because of its flexibility. I'll think about adding it to our internal tooling tech stack, especially the resilience testing is an interesting feature, I think.