question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Expose custom agent parameter?

See original GitHub issue

Since purest allows for it, I would love to get the same support for grant. That would allow me to control the whole oauth + user profile fetching flow.

Namely, I want to use this with a proxy agent like: https://github.com/TooTallNate/node-proxy-agent

This is necessary as nodejs core still haven’t expose an overridable http.globalAgent api yet, sorry for nagging you on this: https://github.com/nodejs/node/issues/23281

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:10 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
simovcommented, Dec 7, 2018

👋 @bitinn I was thinking about this issue, and while I may never expose the underlying HTTP library, I think it will be possible to … well, monkey-patch it:

var https = require('https')
var agent = new https.Agent({})

var request = require('grant/lib/client.js')

require.cache[require.resolve('grant/lib/client.js')].exports = (options) => {
  options.agent = agent
  return request(options)
}

Just make sure you execute this code after Grant has been required.

The options argument expects request-compose options that in turn can be any HTTP option from the underlying Node Core API, including agent.

Let me know what do you think!

1reaction
nathanbrizzeecommented, Jul 15, 2020

@simov, Here are the steps to create a full Feathersjs server with Microsoft Azure OAuth (on Ubuntu 20.04). This shows the code I tried to add to monkey patch the options variable that didn’t work.

  • Install the feathers generator cli with: sudo npm install -g @feathersjs/cli
  • Create a project folder with whatever name you like: mkdir granttest && cd granttest
  • Generate a brand new feathersjs app. (Accept ALL the default prompts): feathers generate app
  • Edit default.json and add the following oauth section (the local section is shown for reference):
  "local": {
      "usernameField": "email",
      "passwordField": "password"
    },
    "oauth": {
      "redirect": "/",
      "microsoft": {
        "activeDirectoryGroups": [],
        "subdomain": "MICROSOFT_DIR_TENANT_ID",
        "key": "MICROSOFT_APP_CLIENT_ID",
        "secret": "MICROSOFT_CLIENT_SECRET",
        "authorize_url": "https://login.microsoftonline.com/[subdomain]/oauth2/v2.0/authorize",
        "access_url": "https://login.microsoftonline.com/[subdomain]/oauth2/v2.0/token",
        "profile_url": "https://graph.microsoft.com/v1.0/me",
        "memberof_url": "https://graph.microsoft.com/v1.0/me/memberOf?$select=displayName",
        "custom_params": {
          "response_type": "code",
          "response_mode": "query",
          "grant_type": "authorization_code"
        },
        "scope": [
          "https://graph.microsoft.com/offline_access",
          "https://graph.microsoft.com/openid",
          "https://graph.microsoft.com/profile",
          "https://graph.microsoft.com/email"
        ],
        "state": true,
        "nonce": true
      }
    }    
  • Edit the file authentication.js and make it look like the following (I added only two lines for azure. The rest was auto generated):
const { AuthenticationService, JWTStrategy } = require('@feathersjs/authentication');
const { LocalStrategy } = require('@feathersjs/authentication-local');
const { expressOauth } = require('@feathersjs/authentication-oauth');
const { AzureADStrategy } = require('./authentication-azure.js');

module.exports = app => {
  const authentication = new AuthenticationService(app);

  authentication.register('jwt', new JWTStrategy());
  authentication.register('local', new LocalStrategy());
  authentication.register('microsoft', new AzureADStrategy());

  app.use('/authentication', authentication);
  app.configure(expressOauth());
};
  • Create a new file called authentication-azure.js in the same folder as authentication.js. Add the following code to it. This is the code I am trying to patch. You will see my code changes near the top. Yes I know I’m not doing anything with the variable proxyAddr . I removed a bunch of code that didn’t do anything but left this here.
const { OAuthStrategy } = require('@feathersjs/authentication-oauth');
const axios = require('axios');
const feathersErrors = require('@feathersjs/errors');
// https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/12345678-1234-1234-12345678901234567/isMSAApp/
// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols

// HTTP/HTTPS proxy to connect to
var proxyAddr = process.env.http_proxy || 'http://localhost:8009';
console.log('using proxy server ', proxyAddr);

// https://github.com/simov/grant/issues/93
var https = require('https');
const moduleName = 'grant/lib/client.js';
var request = require(moduleName);

var agent = new https.Agent({});
const fooa = require.cache[require.resolve(moduleName)]; //eslint-disable-line
console.log(fooa);

require.cache[require.resolve(moduleName)].exports = (options) => {
  options.agent = agent;
  return request(options);
};

const foob = require.cache[require.resolve(moduleName)]; //eslint-disable-line
console.dir(foob);

class AzureADStrategy extends OAuthStrategy {
  async getEntityData(profile) {
    const baseData = await super.getEntityData(profile);
    console.dir(baseData);
    console.dir(profile);

    return {
      ...baseData,
      email: profile.mail,
      firstName: profile.givenName,
      lastName: profile.surname,
      displayName: profile.displayName,
    };
  }

  // https://docs.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0&tabs=http
  async getMemberOfGroups(data, params) {
    const url = this.configuration.memberof_url || '';
    if (!url) {
      throw new feathersErrors.GeneralError(
        'Missing memberof_url in configuration'
      );
    }
    if (params.authenticated === true && data.access_token) {
      try {
        // fetch data from a url endpoint
        const memberInfo = await axios.get(url, {
          headers: {
            'user-agent': 'feathers-azure-authentication',
            Accept: 'application/json',
            Authorization: `Bearer ${data.access_token}`,
          },
        });
        return memberInfo.data || {};
      } catch (error) {
        throw new feathersErrors.GeneralError(error);
      }
    } else {
      throw new feathersErrors.NotAuthenticated(
        'Not authorized - missing access_token'
      );
    }
  }

  async getProfile(data, params) {
    const profile = await super.getProfile(data, params);
    const memberOf = await this.getMemberOfGroups(data, params);
    const ADGroups = memberOf.value || [];

    // If there was a list of AD groups to check for
    let groupFound = true;
    if (
      // Get allowed list from default.json
      this.configuration.activeDirectoryGroups &&
      this.configuration.activeDirectoryGroups.length > 0
    ) {
      groupFound = this.configuration.activeDirectoryGroups.some(
        (groupToCheckFor) => {
          return ADGroups.some((adGroup) => {
            return adGroup.displayName === groupToCheckFor; // Look for an exact match (Case Sensitive).
          });
        }
      );
    }
    if (!groupFound) {
      throw new feathersErrors.NotAuthenticated(
        'User must be a member of one of the valid Azure Active Directory groups for this API set'
      );
    }

    const retval = {
      ...profile,
      memberOf: {
        ...memberOf,
      },
    };
    return retval;
  }
}

exports.AzureADStrategy = AzureADStrategy;

And, if you want to test with Microsoft env vars, a quick hack is to modify the run script in package.json . Note, you’ll have to modify the values of the tenant_id, client_id, and client_secret from your Azure configuration for you app ID and set the redirect back to http://localhost:3030/

  "scripts": {
    "test": "npm run lint && npm run mocha",
    "lint": "eslint src/. test/. --config .eslintrc.json --fix",
    "dev": "MICROSOFT_DIR_TENANT_ID=11111111-2222-3333-4444-555555555555 MICROSOFT_APP_CLIENT_ID=11111111-2222-3333-4444-555555555555 MICROSOFT_CLIENT_SECRET=12345678901234567890123456789012 nodemon src/",
    "start": "node src/",
    "mocha": "mocha test/ --recursive --exit"
  },

Now run the server with npm run dev and visit http://localhost:3030/oauth/microsoft from your browser .

I tried all sorts of different ways to get ahold of the options variable so I could change the proxy but nothing I tried worked. I use VSCode for my testing and used it to debug through the code. If you have any ideas on how to get this to work it would be greatly appreciated. My work proxies all internet access. I want to proxy my feathersjs app with CNTLM on my local box out to the internet because we use an authenticated proxy that has username/password info for Windows. Essentially proxy the proxy. The proxy wreaks havoc with applications that don’t know how to be configured and use the proxy appropriately. The Grant library completely fails since it doesn’t expose the proxy options. This all (Grant library plus FeathersJS) works fine on my laptop at home where there is no proxy. Thank you in advance.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Creating custom agent types
You can create custom agent and resource types. They have their own diagrams where you can add parameters, statecharts, functions, etc.
Read more >
Custom parameters
A custom parameter is a reference to information on your website or mobile app, and collects data exposed on the front-end of the...
Read more >
Changing Build Parameter Type and UI Appearance
The parameter's specification defines how its controls are presented and validated in the Run Custom Build dialog.
Read more >
Clarity on teamcity parameters and its types
There're predefined parameters exposing server build properties, agent properties, agent build properties etc. These parameters are passed to the build as ...
Read more >
AWS Systems Manager Parameter Store
You want to use data/parameters across AWS services without exposing the values as plaintext in commands, functions, agent logs, or CloudTrail logs.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found