Add a generic OAuth2 Authorization Server to Auth0

The most common identity providers are readily available on Auth0's dashboard. However, you can add any other OAuth2 provider using the Custom Social Connections extension.

The fetchUserProfile script

A custom fetchUserProfile script will be called after the user has logged in with the OAuth2 provider. Auth0 will execute this script to call the OAuth2 provider API and get the user profile:

function(access_token, ctx, callback){
  // call the oauth2 provider and return a profile
  // here we are returning a "mock" profile, you can use this to start with to test the flow.
  var profile = {
    user_id: '123',
    given_name: 'Eugenio',
    family_name: 'Pace',
    email: 'eugenio@mail.com'
  };

  callback(null, profile);
}

The access_token parameter is used for authenticating requests to the provider's API.

We recommend using the field names from the normalized profile.

For example, the following code will retrieve the user profile from the GitHub API:

function(access_token, ctx, callback) {
  request.get('https://api.github.com/user', {
      'headers': {
          'Authorization': 'Bearer ' + access_token,
          'User-Agent': 'Auth0'
        }
    }, function(e,r,b){
    if( e ) return callback(e);
    if( r.statusCode !== 200 ) return callback(new Error('StatusCode:'+r.statusCode));
      callback(null,JSON.parse(b));
   });
}

You can filter, add or remove anything from the profile returned from the provider. However, it is recommended that you keep this script as simple as possible. More sophisticated manipulation of user information can be achieved through the use of Rules. One advantage of using Rules is that they apply to any connection.

Log in using the custom connection

You can use any of the Auth0 standard mechanisms (e.g. direct links, Auth0 Lock, auth0.js, etc.) to login a user with the your custom connection.

A direct link would look like:

https://YOUR_AUTH0_DOMAIN/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://YOUR_APP/callback
  &scope=openid%20profile
  &connection=THE_NAME_OF_THE_CONNECTION
  &state=OPAQUE_VALUE

To add a custom connection in Lock, you can add a custom button by following the instructions at Adding custom connections to lock and using this link as the button href.

Pass provider-specific parameters

You can pass provider-specific parameters to the Authorization endpoint of an OAuth 2.0 providers. These can be either static or dynamic.

Pass static parameters

To pass any static parameters, you can use the authParams element of the options when configuring an OAuth 2.0 connection via the Management API.

Let's use WordPress as an example, which allows you to pass an optional blog parameter to their OAuth 2.0 authorization endpoint (see their OAuth 2.0 documentation for more information). Let's assume that you want to always request a user to grant access to the myblog.wordpress.com blog when logging in using WordPress.

Using the Management API, you can configure the WordPress connection to always pass this value in the blog parameter when authorizing a user via WordPress.


curl --request PATCH \
  --url 'https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID' \
  --header 'content-type: application/json' \
  --data '{ "options": { "client_id": "...", "client_secret": "...", "authParams": { "blog": "myblog.wordpress.com" }, "scripts": { "fetchUserProfile": "..." }, "authorizationURL": "https://public-api.wordpress.com/oauth2/authorize", "tokenURL": "https://public-api.wordpress.com/oauth2/token", "scope": "auth" }, "enabled_clients": [ "..." ] }'
var client = new RestClient("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID");
var request = new RestRequest(Method.PATCH);
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID"

	payload := strings.NewReader("{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.patch("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")
  .header("content-type", "application/json")
  .body("{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID",
  "method": "PATCH",
  "headers": {
    "content-type": "application/json"
  },
  "processData": false,
  "data": "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'PATCH',
  url: 'https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID',
  headers: { 'content-type': 'application/json' },
  body: 
   { options: 
      { client_id: '...',
        client_secret: '...',
        authParams: { blog: 'myblog.wordpress.com' },
        scripts: { fetchUserProfile: '...' },
        authorizationURL: 'https://public-api.wordpress.com/oauth2/authorize',
        tokenURL: 'https://public-api.wordpress.com/oauth2/token',
        scope: 'auth' },
     enabled_clients: [ '...' ] },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"options": @{ @"client_id": @"...", @"client_secret": @"...", @"authParams": @{ @"blog": @"myblog.wordpress.com" }, @"scripts": @{ @"fetchUserProfile": @"..." }, @"authorizationURL": @"https://public-api.wordpress.com/oauth2/authorize", @"tokenURL": @"https://public-api.wordpress.com/oauth2/token", @"scope": @"auth" },
                              @"enabled_clients": @[ @"..." ] };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PATCH",
  CURLOPT_POSTFIELDS => "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }",
  CURLOPT_HTTPHEADER => array(
    "content-type: application/json"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("")

payload = "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"

headers = { 'content-type': "application/json" }

conn.request("PATCH", "/YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Patch.new(url)
request["content-type"] = 'application/json'
request.body = "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParams\": { \"blog\": \"myblog.wordpress.com\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"

response = http.request(request)
puts response.read_body
import Foundation

let headers = ["content-type": "application/json"]
let parameters = [
  "options": [
    "client_id": "...",
    "client_secret": "...",
    "authParams": ["blog": "myblog.wordpress.com"],
    "scripts": ["fetchUserProfile": "..."],
    "authorizationURL": "https://public-api.wordpress.com/oauth2/authorize",
    "tokenURL": "https://public-api.wordpress.com/oauth2/token",
    "scope": "auth"
  ],
  "enabled_clients": ["..."]
]

let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Pass dynamic parameters

There are certain circumstances where you may want to pass a dynamic value to OAuth 2.0 Identity Provider. In this case you can use the authParamsMap element of the options to specify a mapping between one of the existing additional parameters accepted by the Auth0 /authorize endpoint to the parameter accepted by the Identity Provider.

Using the same example of WordPress above, let's assume that you want to pass the blog parameter to WordPress, but you want to specify the actual value of the parameter when calling the Auth0 /authorize endpoint.

In this case you can use one of the existing addition parameters accepted by the /authorize endpoint, such as access_type, and map that to the blog parameter being passed to WordPress:


curl --request PATCH \
  --url 'https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID' \
  --header 'content-type: application/json' \
  --data '{ "options": { "client_id": "...", "client_secret": "...", "authParamsMap": { "blog": "access_type" }, "scripts": { "fetchUserProfile": "..." }, "authorizationURL": "https://public-api.wordpress.com/oauth2/authorize", "tokenURL": "https://public-api.wordpress.com/oauth2/token", "scope": "auth" }, "enabled_clients": [ "..." ] }'
var client = new RestClient("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID");
var request = new RestRequest(Method.PATCH);
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID"

	payload := strings.NewReader("{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }")

	req, _ := http.NewRequest("PATCH", url, payload)

	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.patch("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")
  .header("content-type", "application/json")
  .body("{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }")
  .asString();
var settings = {
  "async": true,
  "crossDomain": true,
  "url": "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID",
  "method": "PATCH",
  "headers": {
    "content-type": "application/json"
  },
  "processData": false,
  "data": "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"
}

$.ajax(settings).done(function (response) {
  console.log(response);
});
var request = require("request");

var options = { method: 'PATCH',
  url: 'https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID',
  headers: { 'content-type': 'application/json' },
  body: 
   { options: 
      { client_id: '...',
        client_secret: '...',
        authParamsMap: { blog: 'access_type' },
        scripts: { fetchUserProfile: '...' },
        authorizationURL: 'https://public-api.wordpress.com/oauth2/authorize',
        tokenURL: 'https://public-api.wordpress.com/oauth2/token',
        scope: 'auth' },
     enabled_clients: [ '...' ] },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"options": @{ @"client_id": @"...", @"client_secret": @"...", @"authParamsMap": @{ @"blog": @"access_type" }, @"scripts": @{ @"fetchUserProfile": @"..." }, @"authorizationURL": @"https://public-api.wordpress.com/oauth2/authorize", @"tokenURL": @"https://public-api.wordpress.com/oauth2/token", @"scope": @"auth" },
                              @"enabled_clients": @[ @"..." ] };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"PATCH"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "PATCH",
  CURLOPT_POSTFIELDS => "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }",
  CURLOPT_HTTPHEADER => array(
    "content-type: application/json"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("")

payload = "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"

headers = { 'content-type': "application/json" }

conn.request("PATCH", "/YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'

url = URI("https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Patch.new(url)
request["content-type"] = 'application/json'
request.body = "{ \"options\": { \"client_id\": \"...\", \"client_secret\": \"...\", \"authParamsMap\": { \"blog\": \"access_type\" }, \"scripts\": { \"fetchUserProfile\": \"...\" }, \"authorizationURL\": \"https://public-api.wordpress.com/oauth2/authorize\", \"tokenURL\": \"https://public-api.wordpress.com/oauth2/token\", \"scope\": \"auth\" }, \"enabled_clients\": [ \"...\" ] }"

response = http.request(request)
puts response.read_body
import Foundation

let headers = ["content-type": "application/json"]
let parameters = [
  "options": [
    "client_id": "...",
    "client_secret": "...",
    "authParamsMap": ["blog": "access_type"],
    "scripts": ["fetchUserProfile": "..."],
    "authorizationURL": "https://public-api.wordpress.com/oauth2/authorize",
    "tokenURL": "https://public-api.wordpress.com/oauth2/token",
    "scope": "auth"
  ],
  "enabled_clients": ["..."]
]

let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)

var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/connections/CONNECTION-ID")!,
                                        cachePolicy: .UseProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.HTTPMethod = "PATCH"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData

let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    println(error)
  } else {
    let httpResponse = response as? NSHTTPURLResponse
    println(httpResponse)
  }
})

dataTask.resume()

Now when calling the /authorize endpoint you can pass the name of the blog in the access_type parameter, and that value will in turn be passed along to the WordPress authorization endpoint in the blog parameter:

https://YOUR_AUTH0_DOMAIN/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://YOUR_APP/callback
  &scope=openid%20profile
  &connection=wordpress
  &access_type=myblog.wordpress.com
  &state=OPAQUE_VALUE

Other resources