# Add MSAL to ApolloServer

Install libraries to validate tokens by opening a console in **data-access** project and issuing the following commands

{% code title="/azure-quickstart/data-access/" %}

```bash
npm install jose
npm install openid-client
```

{% endcode %}

In  `/azure-quickstart/data-access/` create the directories `sharedCode/auth` .

In the `auth` directory, create a new file `msal.ts` and add the following code.

{% code title="/azure-quickstart/data-access/shared-code/auth/msal.ts" %}

```typescript
import { Issuer, Client } from 'openid-client';
import {JWT} from 'jose';

var verifyAccessToken = async (context) : Promise<[object, boolean]>  => {
  let token = context.request.headers["authorization"];
  if (!token || !token.startsWith("Bearer ")) return [{}, false];

  token = token.slice(7, token.length).trimLeft(); // Remove 'Bearer ' characters from start of Auth header value

  const settings = {
    audience: process.env.AAD_TOKEN_APPLICATION_ID,
    openIdConfigUrl: process.env.AAD_TOKEN_OPEN_ID_CONNECT_METADATA_DOCUMENT,
    tenantId: process.env.AAD_TOKEN_TENANT_ID
  };

  const issuer = await Issuer.discover(settings.openIdConfigUrl);
  const keyStore = await issuer.keystore();

  var results = JWT.verify(
    token,
    keyStore, 
    {
      audience: settings.audience,
     // issuer: 
        //issuer must remain commented out if you're accepting tokens from :
        // Microsoft's public endpoint (which will be: 'https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0' or it can be any AAD tenant's ID)
        //if you only want to accept local AAD Accounts use: `https://login.microsoftonline.com/${settings.tenantId}/v2.0`
    }
  );
  
  return [
    {
      "authToken": results,
    },
    true
  ];
}

export default {
  VerifyAccessToken: verifyAccessToken
}
```

{% endcode %}

Replace the contents of the main function file with the following to properly reference MSAL and to allow for auth headers.

{% code title="/azure-quickstart/data-access/graphql/index.ts" %}

```typescript
let appInsights = require("applicationinsights");
appInsights.setup().start();
appInsights.defaultClient.commonProperties = {
  environment: process.env.WEBSITE_HOSTNAME,
  functionArea: "graphql"
};
let appInsightsClient = appInsights.defaultClient;

import {
  ApolloServerPlugin,
  GraphQLRequestContext,
  GraphQLRequestListener,
} from 'apollo-server-plugin-base';

import { ApolloServer, gql }  from 'apollo-server-azure-functions';
import MsalAuth from '../shared/auth/msal';
import { HttpRequest, Context } from '@azure/functions';

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: (parent,args,context) => {
      return `Hello world! ${JSON.stringify(context)}`
    },
  },
};

// referenced from https://jeffmagnusson.com/post/graphql-apollo-server-plugins-in-typescript
const appInsightsPlugin = <ApolloServerPlugin & GraphQLRequestListener>{

  // Fires whenever a GraphQL request is received from a client.
  requestDidStart(requestContext:GraphQLRequestContext): GraphQLRequestListener | void{
    appInsightsClient.trackMetric({name: "apollo-query", value: 1});
    return this;
  },
  // Fires for graph exceptions
  didEncounterErrors: function(requestContext:GraphQLRequestContext) {
    appInsightsClient.trackMetric({name: "apollo-error", value: 1});
    appInsightsClient.trackException({exception: new Error("Apollo Error")});
    appInsightsClient.trackException({exception: {category:"Apollo Error", details: requestContext.errors}});
  }
  
}

const getPlaygroundSetting = () => {
  if(process.env.APOLLO_PLAYGROUND_VISIBLE === "true" ){
    if(process.env.APOLLO_PLAYGROUND_ENDPOINT){
      return {endpoint :process.env.APOLLO_PLAYGROUND_ENDPOINT}
    }
    return true
  } else {
    return false
  }
}

const server = new ApolloServer(
  { 
    typeDefs, 
    resolvers, 
    playground: {endpoint:process.env.APOLLO_PLAYGROUND_ENDPOINT},
    plugins: [
      appInsightsPlugin
    ],
    context: async (request) => {
      var [ user, validated ] = await MsalAuth.VerifyAccessToken(request);
      return {user, validated};
    },
  },
);

const graphqlHandler = server.createHandler({
  cors: {
    origin: '*',
    credentials: true,
  },
});

export default (context: Context, req: HttpRequest) => {
  // https://github.com/Azure/azure-functions-host/issues/6013
  req.headers['x-ms-privatelink-id'] = '';
  // apollo-server only reads this specific string
  req.headers['Access-Control-Request-Headers'] = req.headers['Access-Control-Request-Headers'] || req.headers['access-control-request-headers'];
  return graphqlHandler(context, req);
}
```

{% endcode %}

Update the local settings to supply the function with the appropriate values:

{% code title="/azure-quickstart/data-access/local.settings.json" %}

```typescript
{
  ...
  "Values": [
    {
    ...
    "AAD_TOKEN_OPEN_ID_CONNECT_METADATA_DOCUMENT" : "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
    "AAD_TOKEN_APPLICATION_ID" : "<< AAD API APPPLICATION IDENTIFIER>>",
    "AAD_TOKEN_TENANT_ID" : "<< AAD API TENANT ID>>",
    
    "APOLLO_PLAYGROUND_URI" : "http://localhost:7071/api/graphql"
    ...
    }
  ]
  ...
}
```

{% endcode %}

Azure Settings

Open the data-access function App

* In the function app navigate to Configuration&#x20;
  * Choose **+ New Application Setting** for each of the following
    * Name: AAD\_TOKEN\_OPEN\_ID\_CONNECT\_METADATA\_DOCUMENT
      * Value: <\<SAME VALUE AS USED IN LOCAL SETTINGS>>
    * Name: AAD\_TOKEN\_APPLICATION\_ID
      * Value: <\<SAME VALUE AS USED IN LOCAL SETTINGS>>
    * Name: AAD\_TOKEN\_TENANT\_ID
      * Value: <\<SAME VALUE AS USED IN LOCAL SETTINGS>>
    * Name: APOLLO\_PLAYGROUND\_URI
      * Value: <\<AZURE FRONT DOOR URL>>/api/graphql \
        (e.g.: <https://sharethrift><\<random number>>.azurefd.net/api/graphql)
* Choose **Save**
* Choose **Continue** (wait until completion)

Repeat the same steps for the data-access-west function app.

### Important Warnings

{% hint style="warning" %}
**Double Calls to Apollo?** With React.StrictMode enabled (a good thing) you will notice double calls to Apollo ([source](https://github.com/apollographql/apollo-client/issues/6037#issuecomment-659630298)) this is actually intended by the React team ([source](https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects)) to help identify issues to prepare you for React Concurrent, and likely the Apollo team may make changes to better prepare for this. **When you compile for production you likely won't see this behavior.**
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ecfmg.gitbook.io/azure-serverless-quickstart/application-design/adding-authentication/add-msal-to-apolloserver.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
