diff --git a/main/docs.json b/main/docs.json index 1bf450ecb2..038d069a53 100644 --- a/main/docs.json +++ b/main/docs.json @@ -2538,7 +2538,7 @@ { "group": "Backend/API", "pages": [ - "docs/quickstart/backend/nodejs/interactive", + "docs/quickstart/backend/nodejs/index", "docs/quickstart/backend/fastify/index", "docs/quickstart/backend/django/interactive", "docs/quickstart/backend/python/index", @@ -28461,6 +28461,10 @@ "source": "/docs/phpapi-tutorial", "destination": "/docs/quickstart/backend/php" }, + { + "source": "/docs/quickstart/backend/nodejs/interactive", + "destination": "/docs/quickstart/backend/nodejs" + }, { "source": "/docs/quickstart/backend/nodejs/00-getting-started", "destination": "/docs/quickstart/backend/nodejs" diff --git a/main/docs/quickstart/backend/nodejs/02-using.mdx b/main/docs/quickstart/backend/nodejs/02-using.mdx deleted file mode 100644 index e3e95fa5ee..0000000000 --- a/main/docs/quickstart/backend/nodejs/02-using.mdx +++ /dev/null @@ -1,923 +0,0 @@ ---- -title: "Auth0 Node (Express) API SDK Quickstarts: Using your API" ---- -import {AuthCodeBlock} from "/snippets/AuthCodeBlock.jsx"; - -import {AuthCodeGroup} from "/snippets/AuthCodeGroup.jsx"; - -##### By David Patrick - -This tutorial will show you how to use your API. We recommend that you log in to follow this quickstart with examples configured for your account. - -## Calling the API From Your Application - -You can call the API from your application by passing an Access Token in the `Authorization` header of your HTTP request as a Bearer token. - - -```bash lines -curl --request GET \ - --url http://localhost:3010/api/private \ - --header 'authorization: Bearer YOUR_ACCESS_TOKEN' -``` - -```csharp C# lines -var client = new RestClient("http://localhost:3010/api/private"); -var request = new RestRequest(Method.GET); -request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN"); -IRestResponse response = client.Execute(request); -``` - -```go Go lines -package main - -import ( - "fmt" - "net/http" - "io/ioutil" -) - -func main() { - - url := "http://localhost:3010/api/private" - - req, _ := http.NewRequest("GET", url, nil) - - req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN") - - res, _ := http.DefaultClient.Do(req) - - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - - fmt.Println(res) - fmt.Println(string(body)) - -} -``` - -```java Java lines -HttpResponse response = Unirest.get("http://localhost:3010/api/private") - .header("authorization", "Bearer YOUR_ACCESS_TOKEN") - .asString(); -``` - -```javascript Node.JS lines -var axios = require("axios").default; - -var options = { - method: 'GET', - url: 'http://localhost:3010/api/private', - headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN'} -}; - -axios.request(options).then(function (response) { - console.log(response.data); -}).catch(function (error) { - console.error(error); -}); -``` - -```objc Obj-C lines -#import - -NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN" }; - -NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3010/api/private"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10.0]; -[request setHTTPMethod:@"GET"]; -[request setAllHTTPHeaderFields:headers]; - -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]; -``` - -```php PHP lines -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_PORT => "3010", - CURLOPT_URL => "http://localhost:3010/api/private", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "GET", - CURLOPT_HTTPHEADER => [ - "authorization: Bearer YOUR_ACCESS_TOKEN" - ], -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` - -```python Python lines -import http.client - -conn = http.client.HTTPConnection("localhost:3010") - -headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN" } - -conn.request("GET", "/api/private", headers=headers) - -res = conn.getresponse() -data = res.read() - -print(data.decode("utf-8")) -``` - -```ruby Ruby lines -require 'uri' -require 'net/http' - -url = URI("http://localhost:3010/api/private") - -http = Net::HTTP.new(url.host, url.port) - -request = Net::HTTP::Get.new(url) -request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN' - -response = http.request(request) -puts response.read_body -``` - -```swift Swift lines -import Foundation - -let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN"] - -let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:3010/api/private")! as URL, - cachePolicy: .useProtocolCachePolicy, - timeoutInterval: 10.0) -request.httpMethod = "GET" -request.allHTTPHeaderFields = headers - -let session = URLSession.shared -let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - } else { - let httpResponse = response as? HTTPURLResponse - print(httpResponse) - } -}) - -dataTask.resume() -``` - - -## Obtaining an Access Token - -If you are calling the API from a Single-Page Application or a Mobile/Native application, after the authorization flow is completed, you will get an Access Token. How you get the token and how you make the call to the API will be dependent on the type of application you are developing and the framework you are using. For more information refer to the relevant application Quickstarts which contain detailed instructions: - -* [Single-Page Applications](/docs/quickstart/spa) -* [Mobile / Native Application](/docs/quickstart/native) - -If you are calling the API from a command-line tool or another service, where there isn't a user entering their credentials, you need to use the [OAuth Client Credentials flow](https://auth0.com/docs/api/authentication#client-credentials). To do that, register a [Machine to Machine Application](https://manage.auth0.com/#/applications), and then subsequently use the **Client ID** and **Client Secret** of this application when making the request below and pass those along in the `client_id` and `client_secret` parameters respectively. Also include the Audience for the API you want to call. - -Read [Application Settings](https://auth0.com/docs/get-started/dashboard/application-settings) for more information on getting the Client ID and Client Secret for your machine-to-machine app. - - -```bash cURL -curl --request POST \ - --url 'https://{yourDomain}/oauth/token' \ - --header 'content-type: application/x-www-form-urlencoded' \ - --data grant_type=client_credentials \ - --data 'client_id={yourClientId}' \ - --data client_secret={yourClientSecret} \ - --data audience=YOUR_API_IDENTIFIER -``` -```cs C# -var client = new RestClient("https://{yourDomain}/oauth/token"); -var request = new RestRequest(Method.POST); -request.AddHeader("content-type", "application/x-www-form-urlencoded"); -request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER", ParameterType.RequestBody); -IRestResponse response = client.Execute(request); -``` -```go Go -package main - -import ( - "fmt" - "strings" - "net/http" - "io/ioutil" -) - -func main() { - - url := "https://{yourDomain}/oauth/token" - - payload := strings.NewReader("grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER") - - req, _ := http.NewRequest("POST", url, payload) - - req.Header.Add("content-type", "application/x-www-form-urlencoded") - - res, _ := http.DefaultClient.Do(req) - - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - - fmt.Println(res) - fmt.Println(string(body)) - -} -``` -```java Java -HttpResponse response = Unirest.post("https://{yourDomain}/oauth/token") - .header("content-type", "application/x-www-form-urlencoded") - .body("grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER") - .asString(); -``` -```javascript Node.JS -var axios = require("axios").default; - -var options = { - method: 'POST', - url: 'https://{yourDomain}/oauth/token', - headers: {'content-type': 'application/x-www-form-urlencoded'}, - data: new URLSearchParams({ - grant_type: 'client_credentials', - client_id: '{yourClientId}', - client_secret: '{yourClientSecret}', - audience: 'YOUR_API_IDENTIFIER' - }) -}; - -axios.request(options).then(function (response) { - console.log(response.data); -}).catch(function (error) { - console.error(error); -}); -``` -```objc Obj-C -#import - -NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" }; - -NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"grant_type=client_credentials" dataUsingEncoding:NSUTF8StringEncoding]]; -[postData appendData:[@"&client_id={yourClientId}" dataUsingEncoding:NSUTF8StringEncoding]]; -[postData appendData:[@"&client_secret={yourClientSecret}" dataUsingEncoding:NSUTF8StringEncoding]]; -[postData appendData:[@"&audience=YOUR_API_IDENTIFIER" dataUsingEncoding:NSUTF8StringEncoding]]; - -NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/oauth/token"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10.0]; -[request setHTTPMethod:@"POST"]; -[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]; -``` -```php PHP -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_URL => "https://{yourDomain}/oauth/token", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "POST", - CURLOPT_POSTFIELDS => "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER", - CURLOPT_HTTPHEADER => [ - "content-type: application/x-www-form-urlencoded" - ], -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` -```python Python -import http.client - -conn = http.client.HTTPSConnection("") - -payload = "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER" - -headers = { 'content-type': "application/x-www-form-urlencoded" } - -conn.request("POST", "/{yourDomain}/oauth/token", payload, headers) - -res = conn.getresponse() -data = res.read() - -print(data.decode("utf-8")) -``` -```ruby Ruby -require 'uri' -require 'net/http' -require 'openssl' - -url = URI("https://{yourDomain}/oauth/token") - -http = Net::HTTP.new(url.host, url.port) -http.use_ssl = true -http.verify_mode = OpenSSL::SSL::VERIFY_NONE - -request = Net::HTTP::Post.new(url) -request["content-type"] = 'application/x-www-form-urlencoded' -request.body = "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret={yourClientSecret}&audience=YOUR_API_IDENTIFIER" - -response = http.request(request) -puts response.read_body -``` -```swift Swift -import Foundation - -let headers = ["content-type": "application/x-www-form-urlencoded"] - -let postData = NSMutableData(data: "grant_type=client_credentials".data(using: String.Encoding.utf8)!) -postData.append("&client_id={yourClientId}".data(using: String.Encoding.utf8)!) -postData.append("&client_secret={yourClientSecret}".data(using: String.Encoding.utf8)!) -postData.append("&audience=YOUR_API_IDENTIFIER".data(using: String.Encoding.utf8)!) - -let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/token")! as URL, - cachePolicy: .useProtocolCachePolicy, - timeoutInterval: 10.0) -request.httpMethod = "POST" -request.allHTTPHeaderFields = headers -request.httpBody = postData as Data - -let session = URLSession.shared -let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - } else { - let httpResponse = response as? HTTPURLResponse - print(httpResponse) - } -}) - -dataTask.resume() -``` - - -Auth0 customers are billed based on the number of Machine to Machine Access Tokens issued by Auth0. Once your application gets an Access Token it should keep using it until it expires, to minimize the number of tokens requested. - -For testing purposes, you can also get an Access Token from the **Test** tab in your [API settings](https://manage.auth0.com/#/apis). - -## Test Your API - -**1. Calling the secure endpoint** - -You can make a request to the `/api/private` endpoint without passing any Access Token: - - - -```bash cURL lines -curl --request GET \ - --url http://localhost:3010/api/private -``` -```cs C# lines -var client = new RestClient("http://localhost:3010/api/private"); -var request = new RestRequest(Method.GET); -IRestResponse response = client.Execute(request); -``` - -```go Go lines -package main - -import ( - "fmt" - "net/http" - "io/ioutil" -) - -func main() { - - url := "http://localhost:3010/api/private" - - req, _ := http.NewRequest("GET", url, nil) - - res, _ := http.DefaultClient.Do(req) - - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - - fmt.Println(res) - fmt.Println(string(body)) - -} -``` - -```java Java lines -HttpResponse response = Unirest.get("http://localhost:3010/api/private") - .asString(); -``` - -```javascript Node.JS lines -var axios = require("axios").default; - -var options = {method: 'GET', url: 'http://localhost:3010/api/private'}; - -axios.request(options).then(function (response) { - console.log(response.data); -}).catch(function (error) { - console.error(error); -}); -``` - -```objc Obj-C lines -#import - -NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3010/api/private"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10.0]; -[request setHTTPMethod:@"GET"]; - -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]; -``` -```php PHP lines -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_PORT => "3010", - CURLOPT_URL => "http://localhost:3010/api/private", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "GET", -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` - -```python Python lines -import http.client - -conn = http.client.HTTPConnection("localhost:3010") - -conn.request("GET", "/api/private") - -res = conn.getresponse() -data = res.read() - -print(data.decode("utf-8")) -``` - -```ruby Ruby lines -require 'uri' -require 'net/http' - -url = URI("http://localhost:3010/api/private") - -http = Net::HTTP.new(url.host, url.port) - -request = Net::HTTP::Get.new(url) - -response = http.request(request) -puts response.read_body -``` - -```swift Swift lines -import Foundation - -let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:3010/api/private")! as URL, - cachePolicy: .useProtocolCachePolicy, - timeoutInterval: 10.0) -request.httpMethod = "GET" - -let session = URLSession.shared -let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - } else { - let httpResponse = response as? HTTPURLResponse - print(httpResponse) - } -}) - -dataTask.resume() -``` - - - -The API will return a 401 HTTP (Unauthorized) status code: - -![Response for unauthorized API request](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/using/private-unauthorized.png) - -Once again, make the same request but this time pass along the Access Token as a Bearer token in the **Authorization** header of the request: - - -```bash lines -curl --request GET \ - --url http://localhost:3010/api/private \ - --header 'authorization: Bearer YOUR_ACCESS_TOKEN' -``` - -```csharp C# lines -var client = new RestClient("http://localhost:3010/api/private"); -var request = new RestRequest(Method.GET); -request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN"); -IRestResponse response = client.Execute(request); -``` - -```go Go lines -package main - -import ( - "fmt" - "net/http" - "io/ioutil" -) - -func main() { - - url := "http://localhost:3010/api/private" - - req, _ := http.NewRequest("GET", url, nil) - - req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN") - - res, _ := http.DefaultClient.Do(req) - - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - - fmt.Println(res) - fmt.Println(string(body)) - -} -``` - -```java Java lines -HttpResponse response = Unirest.get("http://localhost:3010/api/private") - .header("authorization", "Bearer YOUR_ACCESS_TOKEN") - .asString(); -``` - -```javascript Node.JS lines -var axios = require("axios").default; - -var options = { - method: 'GET', - url: 'http://localhost:3010/api/private', - headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN'} -}; - -axios.request(options).then(function (response) { - console.log(response.data); -}).catch(function (error) { - console.error(error); -}); -``` - -```objc Obj-C lines -#import - -NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN" }; - -NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3010/api/private"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10.0]; -[request setHTTPMethod:@"GET"]; -[request setAllHTTPHeaderFields:headers]; - -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]; -``` - -```php PHP lines -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_PORT => "3010", - CURLOPT_URL => "http://localhost:3010/api/private", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "GET", - CURLOPT_HTTPHEADER => [ - "authorization: Bearer YOUR_ACCESS_TOKEN" - ], -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` - -```python Python lines -import http.client - -conn = http.client.HTTPConnection("localhost:3010") - -headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN" } - -conn.request("GET", "/api/private", headers=headers) - -res = conn.getresponse() -data = res.read() - -print(data.decode("utf-8")) -``` - -```ruby Ruby lines -require 'uri' -require 'net/http' - -url = URI("http://localhost:3010/api/private") - -http = Net::HTTP.new(url.host, url.port) - -request = Net::HTTP::Get.new(url) -request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN' - -response = http.request(request) -puts response.read_body -``` - -```swift Swift lines -import Foundation - -let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN"] - -let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:3010/api/private")! as URL, - cachePolicy: .useProtocolCachePolicy, - timeoutInterval: 10.0) -request.httpMethod = "GET" -request.allHTTPHeaderFields = headers - -let session = URLSession.shared -let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - } else { - let httpResponse = response as? HTTPURLResponse - print(httpResponse) - } -}) - -dataTask.resume() -``` - - -This time the API will return a successful response: - -![Response for authorized API request](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/using/private.png) - -**2. Testing the scoped endpoint** - -To test the endpoint that requires a scope, pass the Access Token containing the correct scope as a Bearer token in the Authorization header: - - - -```bash cURL lines -curl --request GET \ - --url http://localhost:3010/api/private-scoped \ - --header 'authorization: Bearer YOUR_ACCESS_TOKEN' -``` - -```cs C# lines -var client = new RestClient("http://localhost:3010/api/private-scoped"); -var request = new RestRequest(Method.GET); -request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN"); -IRestResponse response = client.Execute(request); -``` - -```go Go lines -package main - -import ( - "fmt" - "net/http" - "io/ioutil" -) - -func main() { - - url := "http://localhost:3010/api/private-scoped" - - req, _ := http.NewRequest("GET", url, nil) - - req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN") - - res, _ := http.DefaultClient.Do(req) - - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - - fmt.Println(res) - fmt.Println(string(body)) - -} -``` - -```java Java lines -HttpResponse response = Unirest.get("http://localhost:3010/api/private-scoped") - .header("authorization", "Bearer YOUR_ACCESS_TOKEN") - .asString(); -``` - -```javascript Node.JS lines -var axios = require("axios").default; - -var options = { - method: 'GET', - url: 'http://localhost:3010/api/private-scoped', - headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN'} -}; - -axios.request(options).then(function (response) { - console.log(response.data); -}).catch(function (error) { - console.error(error); -}); -``` - -```objc Obj-C lines -#import - -NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN" }; - -NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:3010/api/private-scoped"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10.0]; -[request setHTTPMethod:@"GET"]; -[request setAllHTTPHeaderFields:headers]; - -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]; -``` - -```php PHP lines -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_PORT => "3010", - CURLOPT_URL => "http://localhost:3010/api/private-scoped", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "GET", - CURLOPT_HTTPHEADER => [ - "authorization: Bearer YOUR_ACCESS_TOKEN" - ], -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` - -```python Python lines -import http.client - -conn = http.client.HTTPConnection("localhost:3010") - -headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN" } - -conn.request("GET", "/api/private-scoped", headers=headers) - -res = conn.getresponse() -data = res.read() - -print(data.decode("utf-8")) -``` - -```ruby Ruby lines -require 'uri' -require 'net/http' - -url = URI("http://localhost:3010/api/private-scoped") - -http = Net::HTTP.new(url.host, url.port) - -request = Net::HTTP::Get.new(url) -request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN' - -response = http.request(request) -puts response.read_body -``` - -```swift Swift lines -import Foundation - -let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN"] - -let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:3010/api/private-scoped")! as URL, - cachePolicy: .useProtocolCachePolicy, - timeoutInterval: 10.0) -request.httpMethod = "GET" -request.allHTTPHeaderFields = headers - -let session = URLSession.shared -let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - } else { - let httpResponse = response as? HTTPURLResponse - print(httpResponse) - } -}) - -dataTask.resume() -``` - - -If the required scope is present, the API call is successful: - -![Response for scoped API request](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/using/private-scoped.png) - -If the required scope is not present, the API returns a 403 HTTP Status (Forbidden): - -![Response for forbidden scoped API request](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/using/private-scoped-forbidden.png) - -[Edit on GitHub](https://github.com/auth0/docs/edit/master/articles/quickstart/backend/nodejs/02-using.md) \ No newline at end of file diff --git a/main/docs/quickstart/backend/nodejs/03-troubleshooting.mdx b/main/docs/quickstart/backend/nodejs/03-troubleshooting.mdx deleted file mode 100644 index ec90d4bafa..0000000000 --- a/main/docs/quickstart/backend/nodejs/03-troubleshooting.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: "Auth0 Node (Express) API SDK Quickstarts: Troubleshooting" ---- - -##### By David Patrick - -This document will help you troubleshoot your configuration if you get unexpected responses from your API.We recommend that you log in to follow this quickstart with examples configured for your account. - -If you configured JWT validation correctly, you will be able to get proper responses from your API when you make requests. However, in the case where you get a 401 (Unauthorized) response from your API, it is because the configuration of your JWT middleware does not match with the JWT which was passed. - -This document will help you troubleshoot your JWT middleware configuration. - -## How Does a Token Get Validated? - -In terms of validating a JWT, there are various things to consider: - -1. **Is the token well-formed?** In other words, is this token conforming to the structure of a JSON Web Token (JWT)? To get more information on the structure of a JWT, please refer to [this section on the structure of a JWT](/docs/secure/tokens/json-web-tokens#what-is-the-json-web-token-structure-) -2. **Has the token been tampered with?** The last part of a JWT is the signature. The signature is used to verify that the token was in fact signed by the sender and not altered in any way. -3. **Has the token been received in its validity period?** JWTs are only valid for a specified time period (as expressed in the `exp` claim). -4. **Is the token coming from the intended Authority?** This consists of 2 parts - - * **Signature Verification**: Can we confirm that the JWT is correctly signed using the key issued by the issuing authority? - * **Issuer Value**: The Issuer is defined in the `iss` claim. Once again does this claim match up with what your application expects? -5. **Is the token intended for the current application?** So does the `aud` claim of the JWT match with what your application is expecting? - -## Inspecting a Token - -A quick way to inspect a JWT is by using the [JWT.io](https://jwt.io/) website. It has a handy debugger which allows you to quickly check that a JWT is well-formed, and also inspect the values of the various claims. - -![Debugging a JWT on JWT.io](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/aspnet-core-webapi/jwt-io-debugger-rs256.png) - -In the screenshot above you can see that the token was signed using the **RS256** algorithm. The **Issuer** of the token is **https://jerrie.auth0.com/**, and the **Audience** is **https://rs256.test.api**. - -So, in other words, these values in your JWT validation configuration registration must match **exactly** - including the trailing slash for the Issuer, such as - -``` -audience = "https://rs256.test.api", -domain = "https://jerrie.auth0.com/" -}; -``` - - - - - - -For a token signed using HS256, the debugger view will look a little different: - -![Debugging a JWT on JWT.io](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/aspnet-core-webapi/jwt-io-debugger-hs256.png) - -In the screenshot above you can see that the token was signed using the **HS256** algorithm. The **Issuer** of the token is **https://jerrie.auth0.com/**, and the **Audience** is **https://hs256.test.api**. - - - - - - - -[Edit on GitHub](https://github.com/auth0/docs/edit/master/articles/quickstart/backend/nodejs/03-troubleshooting.md) \ No newline at end of file diff --git a/main/docs/quickstart/backend/nodejs/_index.mdx b/main/docs/quickstart/backend/nodejs/_index.mdx deleted file mode 100644 index b1ac19ecf9..0000000000 --- a/main/docs/quickstart/backend/nodejs/_index.mdx +++ /dev/null @@ -1,289 +0,0 @@ ---- -title: Add Authorization to Your Express.js API Application -sidebarTitle: Node (Express) API - ---- -import { Recipe, Content, Section, SideMenu, SideMenuSectionItem, SignUpForm } from "/snippets/recipe.jsx"; -import { LoggedInForm } from "/snippets/Login.jsx"; -import Server from "/snippets/quickstart/backend/nodejs/server.js.mdx"; -import Server2 from "/snippets/quickstart/backend/nodejs/server.js2.mdx"; - -import {AuthCodeGroup} from "/snippets/AuthCodeGroup.jsx"; - -export const sections = [ - { id: "define-permissions", title: "Define permissions" }, - { id: "install-dependencies", title: "Install dependencies" }, - { id: "configure-the-middleware", title: "Configure the middleware" }, - { id: "protect-api-endpoints", title: "Protect API endpoints" } -] - - - - This guide demonstrates how to integrate Auth0 with any new or existing Express.js API application using the - `express-oauth2-jwt-bearer` package. - - If you have not created an API in your Auth0 dashboard yet, use the interactive selector to create a new Auth0 API - or select an existing project API. - - To set up your first API through the Auth0 dashboard, review [our getting started - guide](https://auth0.com/docs/get-started/auth0-overview/set-up-apis). Each Auth0 API uses the API Identifier, which your application needs to validate the access token. - - - **New to Auth0?** Learn [how Auth0 works](https://auth0.com/docs/overview) - and read about [implementing API authentication - and authorization](https://auth0.com/docs/api-auth) using the OAuth 2.0 framework. - - -
- Permissions let you define how resources can be accessed on behalf of the user with a given access token. For - example, you might choose to grant read access to the `messages` resource if users have the manager - access level, and a write access to that resource if they have the administrator access level. - - You can define allowed permissions in the **Permissions** view of the Auth0 Dashboard's [APIs](https://manage.auth0.com/#/apis) section. - - - - - - This example uses the `read:messages` scope. - - - -
- -
- First, install the SDK with `npm`. - - ```bash lines - npm install --save express-oauth2-jwt-bearer - ``` - - - -
- -
- Configure `express-oauth2-jwt-bearer` with your Domain and API Identifier. - - The `checkJwt` middleware shown to the right checks if the user's access token included in the request - is valid. If the token is not valid, the user gets a 401 Authorization error when they try to access the - endpoints. - - The middleware does not check if the token has sufficient scope to access the requested resources. - - -
- -
- To protect an individual route by requiring a valid JWT, configure the route with the `checkJwt` - middleware constructed from `express-oauth2-jwt-bearer`. - - You can configure individual routes to look for a particular scope. To achieve that, set up another middleware - with the `requiresScope` method. Provide the required scopes and apply the middleware to any routes you - want to add authorization to. - - Pass the `checkJwt` and `requiredScopes` middlewares to the route you want to protect. - - In this configuration, only access tokens with the `read:messages` scope can access the endpoint. - - ### Make a Call to Your API - - To make calls to your API, you need an Access Token. You can get an Access Token for testing purposes from the - **Test** view in your [API - settings](https://manage.auth0.com/#/apis). - - - - - - Provide the Access Token as an `Authorization` header in your requests. - - - ```bash cURL lines - curl --request get \ - --url 'http:///%7ByourDomain%7D/api_path' \ - --header 'authorization: Bearer YOUR_ACCESS_TOKEN_HERE' - ``` - - ```csharp C# lines - var client = new RestClient("http:///%7ByourDomain%7D/api_path"); - var request = new RestRequest(Method.GET); - request.AddHeader("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE"); - IRestResponse response = client.Execute(request); - ``` - - ```go Go lines - package main - import ( - "fmt" - "net/http" - "io/ioutil" - ) - func main() { - url := "http:///%7ByourDomain%7D/api_path" - req, _ := http.NewRequest("get", url, nil) - req.Header.Add("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE") - res, _ := http.DefaultClient.Do(req) - defer res.Body.Close() - body, _ := ioutil.ReadAll(res.Body) - fmt.Println(res) - fmt.Println(string(body)) - } - ``` - - ```java Java lines - HttpResponse response = Unirest.get("http:///%7ByourDomain%7D/api_path") - .header("authorization", "Bearer YOUR_ACCESS_TOKEN_HERE") - .asString(); - ``` - - ```javascript Node.JS lines - var axios = require("axios").default; - var options = { - method: 'get', - url: 'http:///%7ByourDomain%7D/api_path', - headers: {authorization: 'Bearer YOUR_ACCESS_TOKEN_HERE'} - }; - axios.request(options).then(function (response) { - console.log(response.data); - }).catch(function (error) { - console.error(error); - }); - ``` - - ```objc Obj-C lines - #import - NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN_HERE" }; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http:///%7ByourDomain%7D/api_path"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - - timeoutInterval:10.0]; - - [request setHTTPMethod:@"get"]; - [request setAllHTTPHeaderFields:headers]; - 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]; - ``` - - ```php PHP lines - #import - NSDictionary *headers = @{ @"authorization": @"Bearer YOUR_ACCESS_TOKEN_HERE" }; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http:///%7ByourDomain%7D/api_path"] - cachePolicy:NSURLRequestUseProtocolCachePolicy - - timeoutInterval:10.0]; - - [request setHTTPMethod:@"get"]; - [request setAllHTTPHeaderFields:headers]; - 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]; - ``` - - ```python Python lines - import http.client - conn = http.client.HTTPConnection("") - headers = { 'authorization': "Bearer YOUR_ACCESS_TOKEN_HERE" } - conn.request("get", "/%7ByourDomain%7D/api_path", headers=headers) - res = conn.getresponse() - data = res.read() - print(data.decode("utf-8")) - ``` - - ```ruby Ruby lines - require 'uri' - require 'net/http' - url = URI("http:///%7ByourDomain%7D/api_path") - http = Net::HTTP.new(url.host, url.port) - request = Net::HTTP::Get.new(url) - request["authorization"] = 'Bearer YOUR_ACCESS_TOKEN_HERE' - response = http.request(request) - puts response.read_body - ``` - - ```swift Swift lines - import Foundation - let headers = ["authorization": "Bearer YOUR_ACCESS_TOKEN_HERE"] - let request = NSMutableURLRequest(url: NSURL(string: "http:///%7ByourDomain%7D/api_path")! as URL, - cachePolicy: .useProtocolCachePolicy, - - timeoutInterval: 10.0) - - request.httpMethod = "get" - request.allHTTPHeaderFields = headers - let session = URLSession.shared - let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in - if (error != nil) { - print(error) - - } else { - let httpResponse = response as? HTTPURLResponse - - print(httpResponse) - - } - }) - dataTask.resume() - ``` - - - - ##### Checkpoint - - Now that you have configured your application, run your application to verify that: - - - `GET /api/public`is available for non-authenticated requests. - - `GET /api/private`is available for authenticated requests. - - `GET /api/private-scoped`is available for authenticated requests containing an access - token with the `read:messages`scope. - - - -
- - ## Next Steps - - Excellent work! If you made it this far, you should now have login, logout, and user profile information running in your application. - - This concludes our quickstart tutorial, but there is so much more to explore. To learn more about what you can do with Auth0, check out: - - * [Auth0 Dashboard](https://manage.auth0.com/dashboard/us/dev-gja8kxz4ndtex3rq) - Learn how to configure and manage your Auth0 tenant and applications - * [express-oauth2-jwt-bearer SDK](https://github.com/auth0/node-oauth2-jwt-bearer/tree/main/packages/express-oauth2-jwt-bearer) - Explore the SDK used in this tutorial more fully - * [Auth0 Marketplace](https://marketplace.auth0.com/) - Discover integrations you can enable to extend Auth0’s functionality -
- - -
diff --git a/main/docs/quickstart/backend/nodejs/index.mdx b/main/docs/quickstart/backend/nodejs/index.mdx index 8fd3af65d7..b076a8a385 100644 --- a/main/docs/quickstart/backend/nodejs/index.mdx +++ b/main/docs/quickstart/backend/nodejs/index.mdx @@ -1,158 +1,697 @@ --- -title: "Node (Express) API: Authorization" +mode: wide +description: This guide demonstrates how to protect Express.js API endpoints using JWT access tokens with the express-oauth2-jwt-bearer SDK. +sidebarTitle: Express API +title: Protect Your Express.js API --- -import {AuthCodeBlock} from "/snippets/AuthCodeBlock.jsx"; + -##### By David Patrick +If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 API authentication automatically in minutes using [agent skills](https://agentskills.io/home). -This tutorial demonstrates how to add authorization to an Express.js API.We recommend that you log in to follow this quickstart with examples configured for your account. +**Install:** -{/* -System requirements: express-oauth2-jwt-bearer 1.0.0 - */} +```bash +npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-express-api +``` - -**New to Auth0?** Learn [how Auth0 works](/docs/get-started/auth0-overview) and read about [implementing API authentication and authorization](/docs/get-started/authentication-and-authorization-flow) using the OAuth 2.0 framework. - +**Then ask your AI assistant:** -## Configure Auth0 APIs +```text +Add Auth0 JWT authentication to my Express API +``` -### Create an API +Your AI assistant will automatically create your Auth0 API, fetch credentials, install `express-oauth2-jwt-bearer`, configure the JWT middleware, and protect your API endpoints with token validation. [Full agent skills documentation →](/quickstart/agent-skills) -In the [APIs](https://manage.auth0.com/#/apis) section of the Auth0 dashboard, click **Create API**. Provide a name and an identifier for your API, for example, `https://quickstarts/api`. You will use the identifier as an `audience` later, when you are configuring the Access Token verification. Leave the **Signing Algorithm** as **RS256**. + -![Create API](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/create-api.png) + + **Prerequisites:** Before you begin, ensure you have the following installed: -By default, your API uses RS256 as the algorithm for signing tokens. Since RS256 uses a private/public keypair, it verifies the tokens against the public key for your Auth0 account. The public key is in the [JSON Web Key Set (JWKS)](/docs/secure/tokens/json-web-tokens/json-web-key-sets) format, and can be accessed [here](https://{yourDomain}/.well-known/jwks.json). + - **[Node.js](https://nodejs.org/en/download)** 18 LTS or newer (supports `^18.12.0 || ^20.2.0 || ^22.1.0 || ^24.0.0`) + - **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 8+ or **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+ or **[pnpm](https://pnpm.io/installation)** 8+ - -We recommend using the default RS256 [signing algorithm](/docs/get-started/applications/signing-algorithms) for your API. If you need to use the HS256 algorithm, see the [HS256 integration sample](https://github.com/auth0-samples/auth0-express-api-samples/tree/master/02-Authorization-HS256). - + Verify installation: `node --version && npm --version` -### Define permissions + **Express Version Compatibility:** This quickstart works with **Express 4.x** and **Express 5.x**. + -Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the `messages` resource if users have the manager access level, and a write access to that resource if they have the administrator access level. +## Get Started -You can define allowed permissions in the **Permissions** view of the Auth0 Dashboard's [APIs](https://manage.auth0.com/#/apis) section. +This quickstart demonstrates how to protect Express.js API endpoints using JWT access tokens. You'll build a secure API that validates Auth0 access tokens, protects routes, and implements scope-based authorization. + + + + Create a new directory for your Express API and initialize a Node.js project. + + ```shellscript + mkdir auth0-express-api && cd auth0-express-api + ``` + + Initialize the project + + ```shellscript + npm init -y + ``` + + Create the project structure + + ```shellscript + touch server.js .env + ``` + + + + Install the required dependencies + + ```shellscript + npm install express express-oauth2-jwt-bearer dotenv + ``` + + Update your `package.json` to add start scripts: + + ```json package.json + { + "name": "auth0-express-api", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "node --watch server.js" + }, + "dependencies": { + "dotenv": "^16.3.1", + "express": "^4.21.0", + "express-oauth2-jwt-bearer": "^1.6.0" + } + } + ``` + + + + Next, you need to create a new API on your Auth0 tenant and add the environment variables to your project. + + You have two options to set up your Auth0 API: use a CLI command or configure manually via the Dashboard: + + + + Run the following command in your project's root directory to create an Auth0 API: + + + ```shellscript Mac + # Install Auth0 CLI (if not already installed) + brew tap auth0/auth0-cli && brew install auth0 + + # Create Auth0 API + auth0 apis create \ + --name "My Express API" \ + --identifier https://my-express-api.example.com + ``` + + ```powershell Windows + # Install Auth0 CLI (if not already installed) + scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git + scoop install auth0 + + # Create Auth0 API + auth0 apis create ` + --name "My Express API" ` + --identifier https://my-express-api.example.com + ``` + + + After creation, copy the **Identifier** and your **Domain** values, then create your `.env` file: + + ```bash .env + AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN + AUTH0_AUDIENCE=YOUR_API_IDENTIFIER + ``` + + + This command will: + 1. Check if you're authenticated (and prompt for login if needed) + 2. Create an Auth0 API with the specified identifier + 3. Display the API details including the domain and identifier + + + + + 1. Go to the [Auth0 Dashboard](https://manage.auth0.com/dashboard/) + 2. Navigate to **Applications** → **APIs** → **Create API** + 3. Enter a name for your API (e.g., "My Express API") + 4. Set the **Identifier** (e.g., `https://my-express-api.example.com`) + - This is your API audience and must be a valid URL format + - It doesn't need to be a real URL - it's just an identifier + 5. Keep **Signing Algorithm** as **RS256** + 6. Click **Create** + 7. Copy the **Identifier** value from the **Settings** tab + + Create your `.env` file with the following values: + + ```bash .env + AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN + AUTH0_AUDIENCE=YOUR_API_IDENTIFIER + ``` + + + Replace `YOUR_AUTH0_DOMAIN` with your Auth0 tenant domain (e.g., `dev-abc123.us.auth0.com`) and `YOUR_API_IDENTIFIER` with your API identifier from the dashboard (e.g., `https://my-express-api.example.com`). + + + + + + Verify your `.env` file exists: `cat .env` (Mac/Linux) or `type .env` (Windows) + + + + + Create your Express server and configure JWT validation: + + ```javascript server.js {1-3,6-7,10-13} lines + require('dotenv').config(); + const express = require('express'); + const { auth } = require('express-oauth2-jwt-bearer'); + + const app = express(); + const port = process.env.PORT || 3001; + + // Configure JWT validation middleware + const checkJwt = auth({ + issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, + audience: process.env.AUTH0_AUDIENCE, + }); + + // Start server + app.listen(port, () => { + console.log(`API server running at http://localhost:${port}`); + }); + ``` + + **What this does:** + - Creates JWT validation middleware using your Auth0 domain and API audience + - Validates the `iss` and `aud` claims on incoming access tokens + - Makes `checkJwt` available for protecting individual routes + + + + Add public and protected routes to your `server.js`: + + ```javascript server.js expandable lines + require('dotenv').config(); + const express = require('express'); + const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); + + const app = express(); + const port = process.env.PORT || 3001; + + // Configure JWT validation middleware + const checkJwt = auth({ + issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, + audience: process.env.AUTH0_AUDIENCE, + }); + + // Public route - no authentication required + app.get('/api/public', (req, res) => { + res.json({ + message: 'Hello from a public endpoint! You don\'t need to be authenticated to see this.', + timestamp: new Date().toISOString(), + }); + }); + + // Protected route - requires valid access token + app.get('/api/private', checkJwt, (req, res) => { + res.json({ + message: 'Hello from a protected endpoint! You successfully authenticated.', + user: req.auth.payload.sub, + timestamp: new Date().toISOString(), + }); + }); + + // Protected route with scope - requires 'read:messages' scope + app.get('/api/private-scoped', checkJwt, requiredScopes('read:messages'), (req, res) => { + res.json({ + message: 'Hello from a scoped endpoint! You have the required permission.', + user: req.auth.payload.sub, + scope: req.auth.payload.scope, + timestamp: new Date().toISOString(), + }); + }); + + // Error handling middleware + app.use((err, req, res, next) => { + const status = err.status || 500; + const message = err.message || 'Internal Server Error'; + + res.status(status).json({ + error: err.code || 'server_error', + message: status === 401 ? 'Authentication required' : message, + }); + }); + + // Start server + app.listen(port, () => { + console.log(`API server running at http://localhost:${port}`); + }); + ``` + + **Key points:** + - Public routes don't require authentication + - Protected routes use the `checkJwt` middleware to require a valid JWT + - Scoped routes use `requiredScopes()` to require specific permissions in the token + - `req.auth.payload` contains the decoded JWT claims for authenticated requests + - The `sub` claim contains the user's unique identifier + + + + Start the development server: + + ```shellscript + npm run dev + ``` + + Your API is now running at [http://localhost:3001](http://localhost:3001). + + + The `--watch` flag in Node.js 18+ automatically restarts the server when files change. + + + + + Test the public endpoint (no authentication required): + + ```bash + curl http://localhost:3001/api/public + ``` + + You should see: + + ```json + { + "message": "Hello from a public endpoint! You don't need to be authenticated to see this.", + "timestamp": "2024-01-15T10:30:00.000Z" + } + ``` + + Test the protected endpoint without a token (should fail): + + ```bash + curl http://localhost:3001/api/private + ``` + + You should see a 401 Unauthorized error: + + ```json + { + "error": "unauthorized", + "message": "Authentication required" + } + ``` + + To test with a valid token: + 1. Go to [Auth0 Dashboard](https://manage.auth0.com/) → **Applications** → **APIs** + 2. Select your API → **Test** tab + 3. Copy the generated access token + + Test your protected endpoint: + + ```bash + curl http://localhost:3001/api/private \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" + ``` + + You should see: + + ```json + { + "message": "Hello from a protected endpoint! You successfully authenticated.", + "user": "auth0|abc123...", + "timestamp": "2024-01-15T10:30:00.000Z" + } + ``` + + + + + **Checkpoint** + + You should now have a protected API. Your API: + 1. Accepts requests to public endpoints without authentication + 2. Rejects requests to protected endpoints without a valid token + 3. Validates JWT tokens against your Auth0 domain and audience + 4. Provides user information from the token claims via `req.auth.payload` + -![Configure Permissions](https://cdn2.auth0.com/docs/1.14550.0/media/articles/server-apis/configure-permissions.png) +--- - -This example uses the `read:messages` scope. - +## Advanced Usage -This example demonstrates: + + Scopes allow fine-grained access control. You can require specific scopes for different endpoints. -* How to check for a JSON Web Token (JWT) in the `Authorization` header of an incoming HTTP request. -* How to check if the token is valid, using the [JSON Web Key Set (JWKS)](/docs/secure/tokens/json-web-tokens/json-web-key-sets) for your Auth0 account. To learn more about validating Access Tokens, see [Validate Access Tokens](/docs/secure/tokens/access-tokens/validate-access-tokens). + **Configure scopes in Auth0:** + 1. In the [Auth0 Dashboard](https://manage.auth0.com/), go to **Applications** → **APIs** → Your API + 2. Navigate to the **Permissions** tab + 3. Add permissions like `read:messages`, `write:messages`, `admin:access` -## Validate Access Tokens + **Protect routes with scopes:** -### Install dependencies + ```javascript server.js + const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); -This guide shows you how to protect an Express API using the [express-oauth2-jwt-bearer](https://github.com/auth0/node-oauth2-jwt-bearer/tree/main/packages/express-oauth2-jwt-bearer) middleware. + // Requires 'read:messages' scope + app.get('/api/messages', checkJwt, requiredScopes('read:messages'), (req, res) => { + res.json({ + messages: [ + { id: 1, text: 'Hello!' }, + { id: 2, text: 'World!' }, + ], + }); + }); -First install the SDK using npm. + // Requires 'admin:access' scope + app.get('/api/admin', checkJwt, requiredScopes('admin:access'), (req, res) => { + res.json({ + message: 'Admin access granted', + userId: req.auth.payload.sub, + }); + }); + ``` + + + If a request lacks the required scope, the API returns `403 Forbidden` with an `insufficient_scope` error. Ensure the client application requests the correct scopes when obtaining an access token. + + + + + Beyond scopes, you can validate custom claims in the JWT payload: + + ```javascript server.js + const { auth, claimEquals, claimIncludes, claimCheck } = require('express-oauth2-jwt-bearer'); + + // Require exact claim value + app.get('/api/org/:orgId', + checkJwt, + claimEquals('org_id', 'org_123'), + (req, res) => { + res.json({ message: 'Organization access granted' }); + } + ); + + // Require claim to include all specified values + app.get('/api/roles', + checkJwt, + claimIncludes('roles', 'editor', 'viewer'), + (req, res) => { + res.json({ message: 'Role check passed' }); + } + ); + + // Custom claim validation logic + app.get('/api/premium', + checkJwt, + claimCheck((claims) => { + return claims.subscription === 'premium' && claims.verified === true; + }), + (req, res) => { + res.json({ message: 'Premium feature access granted' }); + } + ); + ``` + + + Custom claims must use namespaced URLs (e.g., `https://myapp.com/roles`) unless they're standard OIDC claims. [Learn more about custom claims](https://auth0.com/docs/secure/tokens/json-web-tokens/create-custom-claims). + + + + + Allow both authenticated and anonymous access to the same route: + + ```javascript server.js + const optionalAuth = auth({ + issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, + audience: process.env.AUTH0_AUDIENCE, + authRequired: false, + }); -```bash -npm install --save express-oauth2-jwt-bearer -``` + app.get('/api/feed', optionalAuth, (req, res) => { + if (req.auth) { + res.json({ + message: `Welcome back, ${req.auth.payload.sub}!`, + personalizedContent: true, + }); + } else { + res.json({ + message: 'Welcome, guest!', + personalizedContent: false, + }); + } + }); + ``` + + + + Enable CORS to allow requests from web applications: + + ```bash + npm install cors + ``` + + ```javascript server.js + const cors = require('cors'); + + app.use(cors({ + origin: ['http://localhost:3000', 'http://localhost:5173'], + allowedHeaders: ['Authorization', 'Content-Type'], + exposedHeaders: ['WWW-Authenticate'], + })); + ``` + + For production, specify exact origins: + + ```javascript server.js + app.use(cors({ + origin: [ + 'https://myapp.com', + 'https://www.myapp.com' + ], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE'], + })); + ``` + + + + Add comprehensive error handling for authentication errors: + + ```javascript server.js + const { UnauthorizedError, InvalidTokenError, InsufficientScopeError } = require('express-oauth2-jwt-bearer'); + + app.use((err, req, res, next) => { + if (err instanceof InsufficientScopeError) { + return res.status(403).json({ + error: 'forbidden', + message: 'You do not have permission to access this resource', + required_scopes: err.requiredScopes, + }); + } + + if (err instanceof InvalidTokenError) { + return res.status(401).json({ + error: 'invalid_token', + message: 'The provided token is invalid or expired', + }); + } + + if (err instanceof UnauthorizedError) { + return res.status(401).set(err.headers).json({ + error: 'unauthorized', + message: 'Authentication required', + }); + } + + next(err); + }); + ``` + + + For TypeScript projects, install type definitions and configure your project: + ```bash + npm install -D typescript @types/express @types/node + ``` + Create `server.ts`: + ```typescript server.ts + import 'dotenv/config'; + import express, { Request, Response, NextFunction } from 'express'; + import { auth, requiredScopes, UnauthorizedError } from 'express-oauth2-jwt-bearer'; + const app = express(); + const port = process.env.PORT || 3001; -### Configure the middleware + const checkJwt = auth({ + issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, + audience: process.env.AUTH0_AUDIENCE, + }); -Configure `express-oauth2-jwt-bearer` with your Domain and API Identifier. + app.get('/api/public', (req: Request, res: Response) => { + res.json({ message: 'Public endpoint - no authentication required' }); + }); -export const codeExample = `// server.js + app.get('/api/private', checkJwt, (req: Request, res: Response) => { + res.json({ + message: 'Private endpoint', + user: req.auth?.payload.sub, + }); + }); -const express = require('express'); -const app = express(); -const { auth } = require('express-oauth2-jwt-bearer'); + app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + if (err instanceof UnauthorizedError) { + res.status(err.status).set(err.headers).json({ + error: err.code || 'unauthorized', + message: 'Authentication required', + }); + } else { + res.status(500).json({ + error: 'server_error', + message: 'Internal Server Error', + }); + } + }); -// Authorization middleware. When used, the Access Token must -// exist and be verified against the Auth0 JSON Web Key Set. -const checkJwt = auth({ - audience: '{yourApiIdentifier}', - issuerBaseURL: \`https://{yourDomain}/\`, -});`; + app.listen(port, () => { + console.log(`API server running at http://localhost:${port}`); + }); + ``` + + Add a `tsconfig.json`: + + ```json tsconfig.json + { + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "./dist" + }, + "include": ["*.ts"] + } + ``` + + Run with: `npx ts-node server.ts` + - +--- +## Troubleshooting + + + ### "No authorization token was found" + **Problem:** The API cannot find the access token in the request. + **Solutions:** + 1. Ensure the `Authorization` header is present: `Authorization: Bearer YOUR_TOKEN` + 2. Check that "Bearer" is included before the token + 3. Verify the token is not expired + ### "Invalid token" or "jwt malformed" -The `checkJwt` middleware shown above checks if the user's Access Token included in the request is valid. If the token is not valid, the user gets a 401 Authorization error when they try to access the endpoints. The middleware doesn't check if the token has the sufficient scope to access the requested resources. + **Problem:** The token format is invalid. -## Protect API Endpoints + **Solutions:** + 1. Ensure you're using an **access token**, not an ID token + 2. The token should be obtained with your API's `audience` parameter + 3. Check that the token is a valid JWT (should have three parts separated by dots) -The routes shown below are available for the following requests: + ### Unexpected "iss" or "aud" value -* `GET /api/public`: available for non-authenticated requests -* `GET /api/private`: available for authenticated requests containing an access token with no additional scopes -* `GET /api/private-scoped`: available for authenticated requests containing an access token with the `read:messages` scope granted + **Problem:** The issuer or audience in the token doesn't match your configuration. -To protect an individual route that requires a valid JWT, configure the route with the `checkJwt` `express-oauth2-jwt-bearer` middleware. + **Solutions:** + 1. Decode your token at [jwt.io](https://jwt.io) + 2. Check the `iss` claim matches `https://YOUR_AUTH0_DOMAIN/` (note the trailing slash) + 3. Check the `aud` claim matches your `AUTH0_AUDIENCE` exactly + 4. Verify your `.env` values: + ```bash + AUTH0_DOMAIN=dev-abc123.us.auth0.com + AUTH0_AUDIENCE=https://my-express-api.example.com + ``` -```javascript lines -// server.js + ### "You must provide an issuerBaseURL" or "audience is required" -// This route doesn't need authentication -app.get('/api/public', function(req, res) { - res.json({ - message: 'Hello from a public endpoint! You don\'t need to be authenticated to see this.' - }); -}); + **Problem:** Environment variables are not being loaded. -// This route needs authentication -app.get('/api/private', checkJwt, function(req, res) { - res.json({ - message: 'Hello from a private endpoint! You need to be authenticated to see this.' - }); -}); -``` + **Solutions:** + 1. Ensure `.env` file exists in your project root + 2. Verify `dotenv` is installed: `npm install dotenv` + 3. Add `require('dotenv').config()` at the very top of your server file + 4. Check variable names match exactly (case-sensitive) + ### 401 Unauthorized on all requests + **Possible causes:** + - Token is expired + - Audience doesn't match + - Issuer doesn't match + **Debug steps:** + 1. Decode your token at [jwt.io](https://jwt.io) + 2. Check the `exp` claim hasn't passed + 3. Verify `aud` claim matches your `AUTH0_AUDIENCE` exactly + 4. Verify `iss` claim is `https://{AUTH0_DOMAIN}/` + 5. Ensure Authorization header format is `Bearer YOUR_TOKEN` (with space) + ### 403 Forbidden with "insufficient_scope" + **Problem:** The token doesn't have the required scopes. -You can configure individual routes to look for a particular scope. To achieve that, set up another middleware with the `requiresScope` method. Provide the required scopes and apply the middleware to any routes you want to add authorization to. + **Solutions:** + 1. Verify the scopes are defined in your Auth0 API (Dashboard → **Applications** → **APIs** → **Permissions**) + 2. Request the scopes when obtaining the token + 3. Check the token's `scope` claim includes the required scopes -Pass the `checkJwt` and `requiredScopes` middlewares to the route you want to protect. + ### CORS errors in browser -```javascript lines -// server.js -const { requiredScopes } = require('express-oauth2-jwt-bearer'); + **Problem:** Browser blocks API requests due to CORS policy. -const checkScopes = requiredScopes('read:messages'); + **Solution:** Install and configure `cors`: -app.get('/api/private-scoped', checkJwt, checkScopes, function(req, res) { - res.json({ - message: 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.' - }); -}); -``` + ```bash + npm install cors + ``` + ```javascript + const cors = require('cors'); + app.use(cors({ + origin: 'http://localhost:3000', + })); + ``` + + +--- + +## Next Steps +Now that you have a protected API, consider exploring: +- **[Role-Based Access Control](https://auth0.com/docs/manage-users/access-control/rbac)** - Implement fine-grained permissions +- **[API Authorization Best Practices](https://auth0.com/docs/secure/tokens/access-tokens)** - Learn about access token best practices +- **[Monitor Your API](https://auth0.com/docs/deploy-monitor/logs)** - Set up logging and monitoring +- **[Auth0 Community](https://community.auth0.com/)** - Get help from the community -In this configuration, only Access Tokens with the `read:messages` scope can access the endpoint. +--- - -##### What can you do next? -| [Configure other identity providers](/docs/authenticate/identity-providers) | [Enable multifactor authentication](/docs/secure/multi-factor-authentication) | -| --- | --- | -| [Learn about attack protection](/docs/secure/attack-protection) | [Learn about rules](/docs/customize/rules) | -[Edit on GitHub](https://github.com/auth0/docs/edit/master/articles/quickstart/backend/aspnet-core-webapi/01-authorization.md) - +## Resources ---- \ No newline at end of file +- **[express-oauth2-jwt-bearer GitHub](https://github.com/auth0/node-oauth2-jwt-bearer/tree/main/packages/express-oauth2-jwt-bearer)** - Source code and examples +- **[Express.js Documentation](https://expressjs.com/)** - Learn more about Express +- **[Auth0 API Authentication](https://auth0.com/docs/secure/tokens/access-tokens)** - Understanding access tokens +- **[JWT.io](https://jwt.io/)** - Debug and decode JWTs diff --git a/main/docs/quickstart/backend/nodejs/interactive.mdx b/main/docs/quickstart/backend/nodejs/interactive.mdx deleted file mode 100644 index b70414fe8b..0000000000 --- a/main/docs/quickstart/backend/nodejs/interactive.mdx +++ /dev/null @@ -1,787 +0,0 @@ ---- -title: "Secure Express API with Auth0" -sidebarTitle: "Express API" -mode: wide -description: "Protect your Express.js API with JWT authentication using Auth0. Validate access tokens and implement scope-based authorization." ---- - -# Protect Your Express.js API with JWT Authentication - -This guide demonstrates how to add JWT authentication to your Express.js API using the `express-oauth2-jwt-bearer` SDK. You'll validate access tokens issued by Auth0, protect API routes, and implement scope-based authorization. - -> **Prerequisites**: Before you begin, ensure you have: -> - [Node.js](https://nodejs.org/) 18 LTS or newer (supports `^18.12.0 || ^20.2.0 || ^22.1.0 || ^24.0.0`) -> - [npm](https://npmjs.com/) 8+ (or yarn/pnpm) -> - An [Auth0 account](https://auth0.com/signup) (free tier available) -> - Basic familiarity with Express.js - ---- - -## Get Started - -### 1. Create a new Express project - -Create a new directory and initialize a Node.js project: - -```bash -mkdir my-secure-api && cd my-secure-api -``` - -```bash -npm init -y -``` - -### 2. Install dependencies - -Install Express, the authentication SDK, and dotenv for environment variable management: - -```bash -npm install express express-oauth2-jwt-bearer dotenv -``` - -For TypeScript projects, also install type definitions: - -```bash -npm install -D typescript @types/express @types/node -``` - -### 3. Configure your Auth0 API - -You need an Auth0 API to issue access tokens for your Express application. Configure Auth0 using the CLI or manually via the Dashboard: - - - - -If you have the [Auth0 CLI](https://github.com/auth0/auth0-cli) installed, run: - -```bash -auth0 apis create \ - --name "My Express API" \ - --identifier "https://api.example.com" \ - --signing-alg "RS256" -``` - -After creation, note the **Identifier** value—this is your `AUTH0_AUDIENCE`. - - - - -1. Go to the [Auth0 Dashboard](https://manage.auth0.com/) -2. Navigate to **Applications** → **APIs** in the sidebar -3. Click **+ Create API** -4. Fill in the following: - - **Name**: My Express API (or your preferred name) - - **Identifier**: `https://api.example.com` (this becomes your `AUTH0_AUDIENCE`) - - **Signing Algorithm**: RS256 (recommended) -5. Click **Create** -6. Copy the **Identifier** value for use in your application - - - - -### 4. Create environment configuration - -Create a `.env` file in your project root with your Auth0 configuration: - -```bash -# .env - -# Your Auth0 tenant domain (without https:// prefix shown in Dashboard) -AUTH0_AUDIENCE=https://api.example.com -AUTH0_DOMAIN=your-tenant.us.auth0.com - -# Server configuration -PORT=3000 -``` - -> ℹ️ **Finding your Auth0 Domain**: In the Auth0 Dashboard, your domain appears in the top-left corner or under **Settings** → **General**. It typically looks like `dev-abc123.us.auth0.com`. - -| Variable | Description | Example | -|----------|-------------|---------| -| `AUTH0_DOMAIN` | Your Auth0 tenant domain | `dev-abc123.us.auth0.com` | -| `AUTH0_AUDIENCE` | The API Identifier you created | `https://api.example.com` | - -### 5. Create the server - -Create `server.js` (or `server.ts` for TypeScript) with the following code: - -```javascript -// server.js -require('dotenv').config(); -const express = require('express'); -const { auth } = require('express-oauth2-jwt-bearer'); - -const app = express(); -const port = process.env.PORT || 3000; - -// Configure JWT validation middleware -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, -}); - -// Public route - no authentication required -app.get('/api/public', (req, res) => { - res.json({ - message: 'Hello from a public endpoint! No authentication required.', - }); -}); - -// Protected route - requires valid JWT -app.get('/api/private', checkJwt, (req, res) => { - res.json({ - message: 'Hello from a private endpoint!', - user: req.auth.payload.sub, - }); -}); - -// Error handling middleware -app.use((err, req, res, next) => { - console.error(err.stack); - - const status = err.status || 500; - const message = err.message || 'Internal Server Error'; - - res.status(status).json({ - error: err.code || 'unauthorized', - message: status === 401 ? 'Authentication required' : message, - }); -}); - -app.listen(port, () => { - console.log(`API server running on http://localhost:${port}`); -}); -``` - -
-TypeScript Version - -Create `server.ts`: - -```typescript -// server.ts -import 'dotenv/config'; -import express, { Request, Response, NextFunction } from 'express'; -import { auth, UnauthorizedError } from 'express-oauth2-jwt-bearer'; - -const app = express(); -const port = process.env.PORT || 3000; - -// Configure JWT validation middleware -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, -}); - -// Public route - no authentication required -app.get('/api/public', (req: Request, res: Response) => { - res.json({ - message: 'Hello from a public endpoint! No authentication required.', - }); -}); - -// Protected route - requires valid JWT -app.get('/api/private', checkJwt, (req: Request, res: Response) => { - res.json({ - message: 'Hello from a private endpoint!', - user: req.auth?.payload.sub, - }); -}); - -// Error handling middleware -app.use((err: Error, req: Request, res: Response, next: NextFunction) => { - console.error(err.stack); - - if (err instanceof UnauthorizedError) { - res.status(err.status).set(err.headers).json({ - error: err.code || 'unauthorized', - message: 'Authentication required', - }); - } else { - res.status(500).json({ - error: 'server_error', - message: 'Internal Server Error', - }); - } -}); - -app.listen(port, () => { - console.log(`API server running on http://localhost:${port}`); -}); -``` - -Add a `tsconfig.json`: - -```json -{ - "compilerOptions": { - "target": "ES2020", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "./dist" - }, - "include": ["*.ts"] -} -``` - -
- -### 6. Run your API - -Start the server: - -```bash -node server.js -``` - -For TypeScript: - -```bash -npx ts-node server.ts -``` - -> ℹ️ Your API is now running at [http://localhost:3000](http://localhost:3000). - -> ✅ **Checkpoint**: Verify your API is protecting routes correctly: -> -> **Test the public endpoint** (should return 200): -> ```bash -> curl http://localhost:3000/api/public -> ``` -> -> **Test the private endpoint without a token** (should return 401): -> ```bash -> curl http://localhost:3000/api/private -> ``` -> -> You should receive: -> - Public endpoint: `{"message":"Hello from a public endpoint!..."}` -> - Private endpoint: `{"error":"unauthorized","message":"Authentication required"}` - -### 7. Test with a valid token - -To test the protected endpoint with a valid access token: - -1. Go to [Auth0 Dashboard](https://manage.auth0.com/) → **Applications** → **APIs** -2. Select your API → **Test** tab -3. Copy the generated access token - -Test your protected endpoint: - -```bash -curl http://localhost:3000/api/private \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -``` - -You should receive a response like: - -```json -{ - "message": "Hello from a private endpoint!", - "user": "auth0|abc123..." -} -``` - -> ✅ **Success!** Your Express API is now protected with JWT authentication. - ---- - -## Add Scope-Based Authorization - -Scopes allow fine-grained access control. You can require specific scopes for different endpoints. - -### 1. Configure scopes in Auth0 - -1. In the Auth0 Dashboard, go to **Applications** → **APIs** → Your API -2. Navigate to the **Permissions** tab -3. Add the following scopes: - - `read:messages` - Read messages - - `write:messages` - Write messages - - `admin:access` - Administrative access - -### 2. Protect routes with scopes - -Update your server to use scope-based authorization: - -```javascript -const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); - -// ... existing setup ... - -// Requires 'read:messages' scope -app.get('/api/messages', checkJwt, requiredScopes('read:messages'), (req, res) => { - res.json({ - messages: [ - { id: 1, text: 'Hello!' }, - { id: 2, text: 'World!' }, - ], - }); -}); - -// Requires 'admin:access' scope -app.get('/api/admin', checkJwt, requiredScopes('admin:access'), (req, res) => { - res.json({ - message: 'Admin access granted', - userId: req.auth.payload.sub, - }); -}); -``` - -### 3. Request tokens with scopes - -When obtaining tokens, include the required scopes in the authorization request. The scopes granted will be included in the token's `scope` claim. - -> ⚠️ **Important**: If a request lacks the required scope, the API returns `403 Forbidden` with an `insufficient_scope` error. - ---- - -## Add Claim Validation - -Beyond scopes, you can validate custom claims in the JWT payload. - -### Validate specific claim values - -```javascript -const { auth, claimEquals, claimIncludes, claimCheck } = require('express-oauth2-jwt-bearer'); - -// Require exact claim value -app.get('/api/org/:orgId', - checkJwt, - claimEquals('org_id', 'org_123'), - (req, res) => { - res.json({ message: 'Organization access granted' }); - } -); - -// Require claim to include all specified values -app.get('/api/roles', - checkJwt, - claimIncludes('roles', 'editor', 'viewer'), - (req, res) => { - res.json({ message: 'Role check passed' }); - } -); - -// Custom claim validation logic -app.get('/api/premium', - checkJwt, - claimCheck((claims) => { - return claims.subscription === 'premium' && claims.verified === true; - }), - (req, res) => { - res.json({ message: 'Premium feature access granted' }); - } -); -``` - ---- - -## Troubleshooting - -
-Error: "Unexpected 'iss' value" or "Unexpected 'aud' value" - -**Cause**: The issuer or audience in the token doesn't match your configuration. - -**Solution**: - -1. Decode your token at [jwt.io](https://jwt.io) -2. Check the `iss` claim matches `https://YOUR_AUTH0_DOMAIN/` (note the trailing slash) -3. Check the `aud` claim matches your `AUTH0_AUDIENCE` exactly - -```bash -# Correct .env configuration -AUTH0_DOMAIN=dev-abc123.us.auth0.com -AUTH0_AUDIENCE=https://api.example.com -``` - -
- -
-Error: "You must provide an 'issuerBaseURL'" or "'audience' is required" - -**Cause**: Environment variables are not being loaded. - -**Solution**: - -1. Ensure `.env` file exists in your project root -2. Verify `dotenv` is installed: `npm install dotenv` -3. Add `require('dotenv').config()` at the top of your server file -4. Check variable names match exactly (case-sensitive) - -
- -
-401 Unauthorized on all requests - -**Possible causes**: -- Token is expired -- Audience doesn't match -- Issuer doesn't match -- Token not properly formatted - -**Debug steps**: - -1. Decode your token at [jwt.io](https://jwt.io) -2. Check the `exp` claim hasn't passed (token not expired) -3. Verify `aud` claim matches your `AUTH0_AUDIENCE` exactly -4. Verify `iss` claim is `https://{AUTH0_DOMAIN}/` -5. Ensure Authorization header format is `Bearer YOUR_TOKEN` (with space) - -
- -
-Error: "Failed to fetch authorization server metadata" - -**Cause**: The SDK cannot reach Auth0 to fetch OIDC configuration. - -**Solution**: - -1. Verify your `AUTH0_DOMAIN` is correct -2. Check network connectivity to Auth0 -3. If behind a proxy, configure the HTTP agent: - -```javascript -const { HttpsProxyAgent } = require('https-proxy-agent'); - -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, - agent: new HttpsProxyAgent(process.env.HTTPS_PROXY), -}); -``` - -
- -
-403 Forbidden with "insufficient_scope" - -**Cause**: The token doesn't have the required scopes. - -**Solution**: - -1. Verify the scopes are defined in your Auth0 API (Dashboard → APIs → Permissions) -2. Request the scopes when obtaining the token -3. Check the token's `scope` claim includes the required scopes - -
- ---- - -## Advanced Usage - -
-Optional Authentication (Mixed Public/Private Routes) - -Allow both authenticated and anonymous access to the same route: - -```javascript -const optionalAuth = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, - authRequired: false, // Don't reject anonymous requests -}); - -app.get('/api/feed', optionalAuth, (req, res) => { - if (req.auth) { - // Authenticated user - show personalized content - res.json({ - message: `Welcome back, ${req.auth.payload.sub}!`, - personalizedContent: true, - }); - } else { - // Anonymous user - show public content - res.json({ - message: 'Welcome, guest!', - personalizedContent: false, - }); - } -}); -``` - -
- -
-Using Express Router for API Organization - -Organize routes with Express Router: - -```javascript -const express = require('express'); -const { auth, requiredScopes } = require('express-oauth2-jwt-bearer'); - -const app = express(); -const apiRouter = express.Router(); - -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, -}); - -// Apply auth to all API routes -apiRouter.use(checkJwt); - -apiRouter.get('/users', (req, res) => { - res.json({ users: [] }); -}); - -apiRouter.get('/admin', requiredScopes('admin:access'), (req, res) => { - res.json({ admin: 'data' }); -}); - -// Mount router -app.use('/api', apiRouter); -``` - -
- -
-CORS Configuration for SPAs - -Configure CORS when your API is called from a Single Page Application: - -```javascript -const cors = require('cors'); - -app.use(cors({ - origin: 'https://your-spa.example.com', - allowedHeaders: ['Authorization', 'Content-Type'], - exposedHeaders: ['WWW-Authenticate'], // Important for error handling -})); - -app.use(checkJwt); -``` - -Install cors: `npm install cors` - -
- -
-Custom Error Responses - -Customize error responses for better client handling: - -```javascript -const { UnauthorizedError, InvalidTokenError, InsufficientScopeError } = require('express-oauth2-jwt-bearer'); - -app.use((err, req, res, next) => { - if (err instanceof InsufficientScopeError) { - return res.status(403).json({ - error: 'forbidden', - message: 'You do not have permission to access this resource', - required_scopes: err.requiredScopes, - }); - } - - if (err instanceof InvalidTokenError) { - return res.status(401).json({ - error: 'invalid_token', - message: 'The provided token is invalid or expired', - }); - } - - if (err instanceof UnauthorizedError) { - return res.status(401).set(err.headers).json({ - error: 'unauthorized', - message: 'Authentication required', - }); - } - - next(err); -}); -``` - -
- -
-Production Security Best Practices - -For production deployments, add these security enhancements: - -```javascript -const helmet = require('helmet'); - -// Security headers -app.use(helmet()); - -// Trust proxy (when behind load balancer/reverse proxy) -app.enable('trust proxy'); - -// Configure auth with additional options -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, - clockTolerance: 10, // Allow 10 seconds clock skew -}); -``` - -Install helmet: `npm install helmet` - -**Additional recommendations**: -- Always use HTTPS in production -- Set short token expiration times -- Use DPoP for high-security APIs -- Never log or expose complete tokens -- Validate the `audience` claim (SDK does this by default) - -
- -
-Working with Custom Claims in TypeScript - -Define types for custom claims in your tokens: - -```typescript -import { JWTPayload } from 'express-oauth2-jwt-bearer'; - -// Extend the payload type with your custom claims -interface CustomPayload extends JWTPayload { - permissions: string[]; - org_id: string; - subscription: 'free' | 'premium'; -} - -app.get('/api/profile', checkJwt, (req, res) => { - // Type assertion for custom claims - const payload = req.auth?.payload as CustomPayload; - - res.json({ - userId: payload.sub, - organization: payload.org_id, - subscription: payload.subscription, - permissions: payload.permissions, - }); -}); -``` - -
- ---- - -## Complete Example - -Here's a complete Express API with all features: - -```javascript -// server.js -require('dotenv').config(); -const express = require('express'); -const cors = require('cors'); -const { - auth, - requiredScopes, - claimCheck, - UnauthorizedError, - InsufficientScopeError, -} = require('express-oauth2-jwt-bearer'); - -const app = express(); -const port = process.env.PORT || 3000; - -// Middleware -app.use(cors({ - origin: process.env.ALLOWED_ORIGIN || '*', - exposedHeaders: ['WWW-Authenticate'], -})); -app.use(express.json()); - -// JWT validation middleware -const checkJwt = auth({ - issuerBaseURL: `https://${process.env.AUTH0_DOMAIN}`, - audience: process.env.AUTH0_AUDIENCE, -}); - -// Public endpoints -app.get('/api/public', (req, res) => { - res.json({ message: 'Public endpoint - no authentication required' }); -}); - -app.get('/health', (req, res) => { - res.json({ status: 'healthy' }); -}); - -// Protected endpoints -app.get('/api/private', checkJwt, (req, res) => { - res.json({ - message: 'Private endpoint', - user: req.auth.payload.sub, - }); -}); - -// Scope-protected endpoint -app.get('/api/messages', checkJwt, requiredScopes('read:messages'), (req, res) => { - res.json({ - messages: [ - { id: 1, text: 'Hello from the API!' }, - ], - }); -}); - -// Admin endpoint with scope and claim check -app.get('/api/admin', - checkJwt, - requiredScopes('admin:access'), - claimCheck((claims) => claims.role === 'admin'), - (req, res) => { - res.json({ - message: 'Admin access granted', - userId: req.auth.payload.sub, - }); - } -); - -// Error handling -app.use((err, req, res, next) => { - console.error('Error:', err.message); - - if (err instanceof InsufficientScopeError) { - return res.status(403).json({ - error: 'insufficient_scope', - message: 'Missing required permissions', - }); - } - - if (err instanceof UnauthorizedError) { - return res.status(err.status).set(err.headers).json({ - error: err.code || 'unauthorized', - message: 'Authentication required', - }); - } - - res.status(500).json({ - error: 'server_error', - message: 'An unexpected error occurred', - }); -}); - -app.listen(port, () => { - console.log(`🚀 API server running at http://localhost:${port}`); - console.log(` - Public: http://localhost:${port}/api/public`); - console.log(` - Private: http://localhost:${port}/api/private`); -}); -``` - ---- - -## Next Steps - -Now that your Express API is protected with JWT authentication, you can: - -- **Add more protected routes** with different scope requirements -- **Implement refresh token rotation** for long-lived sessions -- **Add rate limiting** to protect against abuse -- **Connect a frontend application** that obtains tokens from Auth0 -- **Enable DPoP** for enhanced token security (proof-of-possession) - -### Useful Resources - -- [express-oauth2-jwt-bearer README](https://github.com/auth0/node-oauth2-jwt-bearer/tree/main/packages/express-oauth2-jwt-bearer) -- [Auth0 API Documentation](https://auth0.com/docs/api) -- [JWT.io](https://jwt.io/) - Debug and decode JWTs -- [OAuth 2.0 RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750) - Bearer Token Usage - ---- - -> ✅ **Congratulations!** You've successfully protected your Express.js API with JWT authentication using Auth0 and express-oauth2-jwt-bearer. \ No newline at end of file