Documentation

JWT Authentication with an API in the DHCS Developer Portal

Overview

This document outlines how to authenticate using OAuth 2.0 to retrieve a JWT (JSON Web Token) and how to use that token to call secured APIs hosted on the DHCS API Gateway. It includes JWT token retrieval examples with curl and C#. Also, once the token is retrieved it includes examples in both curl and C# for leveraging the token to access an API.

Curl example

Step 1: Retrieve an Access Token Using Client Credentials

Use the following curl command to request an access token from the authorization server:

curl -X POST "https://login.mes.dhcs.ca.gov:443/am/oauth2/alpha/access_token" \
  -u <client_id>:<client_secret> \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" -s

Parameters

Parameter Description
client_id Your registered OAuth client ID
client_secret Your OAuth client secret
grant_type Must be set to client_credentials
Content-Type Must be application/x-www-form-urlencoded

Example successful response

{
  "access_token": "<access_token>",
  "scope": "profile",
  "token_type": "Bearer",
  "expires_in": 43199
}

Step 2: Use the Access Token to Call Secured APIs

Include the token in the Authorization header when calling APIs on the Gateway:

curl -X GET \
  https://example-api.gateway.mes.dhcs.ca.gov/api/v1/desired-endpoint \
  -H "Authorization: Bearer <access_token>"

Replace <access_token> with the actual token retrieved in Step 1.

Example API call

ACCESS_TOKEN=<access_token>
curl -X GET \
  https://example-api.gateway.mes.dhcs.ca.gov/api/v1/health \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Determining the URL for the API you want to consume

WAIT! Where did example-api.gateway.mes.dhcs.ca.gov come from in the example? How would I find or build the URL for the API I want to consume?

The API’s base URL (scheme and host) is shown at the top of the API Details page. To call a specific endpoint, append the path from the Swagger documentation to this base URL.

Building the correct URL for your API

  1. Find the base URL (scheme + host):
    This is shown at the top of the API Details page in the DHCS Developer Portal.
  2. Find the endpoint path:
    Use the Swagger documentation for the API.
    Each operation (GET, POST, etc.) has a path (e.g., /api/v1/Health).
  3. Combine them:
    Concatenate the base URL (1) and the endpoint path (2).
    Example:
    • Base URL: https://example-api.gateway.mes.dhcs.ca.gov
    • Endpoint path: /api/v1/Health
    • Full URL: https://example-api.gateway.mes.dhcs.ca.gov/api/v1/Health

.NET example

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
...
public class TestAuth
{
    ...
    [Fact]
    public async Task TestGetJwtTokenAndAuthenticatedEndpointResult()
    {
        // Arrange
        var pingOauthUrl = "https://login.mes.dhcs.ca.gov:443/am/oauth2/alpha/access_token";
        var clientId = "<client_id>";
        var clientSecret = "<client_secret>";
        var endpointUrl = "https://ess-api.gateway.mes.dhcs.ca.gov/api/v1/health";

        // Act
        var jwtToken = await GetJwtToken(pingOauthUrl, clientId, clientSecret);
        var authenticatedEndpointResult = await GetAuthenticatedEndpointAsync(new HttpClient(), jwtToken, endpointUrl);

        // Assert
        Assert.NotNull(jwtToken);
        Assert.NotNull(authenticatedEndpointResult);
    }

    public static async Task<string?> GetJwtToken(string pingOauthUrl, string clientId, string clientSecret)
    {
        using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        var basicAuth64 = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{clientId}:{clientSecret}"));
        using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, pingOauthUrl)
        {
            Content = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("grant_type", "client_credentials"),
            })
        };
        httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth64);
        httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        using var pingJwtResponse = await httpClient.SendAsync(httpRequestMessage);
        var pingJwtResponseBody = await pingJwtResponse.Content.ReadAsStringAsync();
        JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower // snake_case to PascalCase
        };
        var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(pingJwtResponseBody, jsonSerializerOptions);
        return tokenResponse?.AccessToken;
    }

    // Mapping class for jwt token response from Ping Oauth
    private sealed class TokenResponse
    {
        public string? AccessToken { get; set; }
        public int ExpiresIn { get; set; }
        public string? TokenType { get; set; }
        public string? Scope { get; set; }
    }

    public static async Task<string> GetAuthenticatedEndpointAsync(HttpClient http, string accessToken, string endpointUrl, CancellationToken ct = default)
    {
        using var req = new HttpRequestMessage(HttpMethod.Get, endpointUrl);
        req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        using var resp = await http.SendAsync(req, ct);
        var body = await resp.Content.ReadAsStringAsync(ct);
        if (!resp.IsSuccessStatusCode)
            throw new HttpRequestException($"Request failed with {(int)resp.StatusCode}: {body}");
        return body;
    }
}

Conclusion

By following this guide, developers can authenticate securely and call DHCS API Gateway endpoints using bearer tokens. Expect JWT tokens to expire regularly but as this document demonstrates, generating a new JWT token is straightforward. Please consult Ping’s OAuth response expires_in field for more specifics when acquiring the JWT token.