{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/machine/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Quickstart: Workload authentication","description":"Everything about Mosaic Journeys, SDKs, and APIs","siteUrl":"https://developer.transmitsecurity.com/","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"quickstart-workload-authentication","__idx":0},"children":["Quickstart: Workload authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This guide describes how to integrate your workload with Mosaic's Machine Identity Management (MIM) services for workload authentication scenarios. This allows you to secure workload-to-workload communication using JWT tokens that are generated, stored, and protected by Mosaic."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-it-works","__idx":1},"children":["How it works"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's an example of a flow that represents the integration steps below."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/jwt_auth_flow.f4b979ba96a6f7b50cd2a505552f37f5f9dd844e01cc5f39962bc732d80dae8f.0940e27a.png","alt":""},"children":[]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The workload runs the MIM client app for the first time, passing a registration code (ticket) obtained from the Admin Portal (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-2-create-a-ticket"},"children":["Step 2"]}," and ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-3-run-the-client"},"children":["Step 3"]},"). In response, the MIM client initiates a workload registration process with the MIM IDP. This process creates a new workload identity (workload ID) and generates a cryptographic key pair for the workload that will be used by the MIM client to authenticate in all requests to the MIM IDP."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["While the MIM client runs on the machine, it periodically fetches an ID token from the MIM IDP and stores it so it will be available for the workloaded when needed. ID tokens are signed by the MIM IDP's private key."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When the workload needs to authenticate a request to another machine, it obtains an ID token from the MIM client by invoking a local API (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-4-get-id-tokens"},"children":["Step 4"]},")."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The machine that receives the authenticated request validates the ID token using the MIM IDP's public key obtained from the JWKS endpoint (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-5-validate-id-tokens"},"children":["Step 5"]},"). Public keys should be cached for future requests."]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"before-you-start","__idx":2},"children":["Before you start"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before you start your integration with Mosaic, you'll need:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A Mosaic tenant and access to the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://portal.transmitsecurity.io/"},"children":["Admin Portal"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your MIM IDP hostname, provided by Mosaic and unique for your tenant"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["To allowlist Mosaic IPs and domains, if relevant (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/quick_start/enable_communication"},"children":["Learn more"]},")"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1-download-the-client","__idx":3},"children":["Step 1: Download the client"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To integrate with Mosaic, your workload runs a client application called ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["tsmim"]}," that exposes locally-running processes. From ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Machine Identity"]}," > ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://portal.identity.security/machineIdentity/inventory"},"children":["Inventory"]},", download the client (Linux AMD64 version) using the link at the bottom of the page."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/download_client.ebede06db669d23dddd1190b911bb5bf731b8211b8223369aa9b6c585e9d94cf.0940e27a.png","alt":""},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2-create-a-ticket","__idx":4},"children":["Step 2: Create a ticket"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To run the MIM client for the first time, the workload will need a ticket. You can think of it as a registration code that authorizes the workload to run the client and establish the workload's identity in the Mosaic system."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create a ticket from ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Machine Identity"]}," > ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Access Control"]}," > ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://portal.identity.security/machineIdentity/access-control/tickets"},"children":["Tickets"]}," tab (use defaults for the other settings):"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["For ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Description"]},", add a workload description that will appear in the Admin Portal"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["For ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Expiry Date"]},", set the ticket expiration. You'll need a valid ticket the first time you run the client."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["For ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Network Access List"]},", select ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["public-access"]}," for simplicity. The workload's IP must be within these IP ranges."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["For ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Tags"]},", select any tag since they aren't used for workload authentication scenarios."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once a new ticket is created, you can copy the ticket ID directly from the table."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Note"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ticket IDs should be treated as sensitive information, often kept in memory only."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/create_ticket.baf333eadc200c8096d5e53fe656dd86268afa8c5e4a6c31bd87a53140c2113c.0940e27a.png","alt":""},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3-run-the-client","__idx":5},"children":["Step 3: Run the client"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To run the client for the first time, copy the client binary file (from Step 1) to your local machine and use the command below. Replace ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<ticket ID>"]}," with the ticket ID created in Step 2 and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<your-mim-host>"]}," with your MIM IDP hostname provided by Mosaic (format is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<value>.mim.transmitsecurity.io"]},")."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If successful, the MIM client will continue to run until it is interrupted (Ctrl-C). You can see the activity event from the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://portal.identity.security/machineIdentity/activity-details"},"children":["Activity"]}," page of the Admin Portal. For consequent invocations of the client, just omit the invite code from the command."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"$ MIM_OPERATOR_BASE=https://<your-mim-host>/operator ./tsmim run --invite-code <ticket ID>\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Note"},"children":[{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["To run a test scenario using docker-compose, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/machine/test_with_docker"},"children":["Test with Docker-Compose"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you're having issues, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/machine/troubleshooting"},"children":["Troubleshooting"]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-4-get-id-tokens","__idx":6},"children":["Step 4: Get ID tokens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When the client is running on your workload instance, it will obtain ID tokens from the MIM IDP at regular intervals and store them. The workload can locally fetch an ID token when needed using its ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/openapi/machine/workloads.openapi/other/getjwtidtoken"},"children":["local API"]},", as shown below. The result includes an encoded JWT, which can be used by other workloads to verify this workload's identity."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"shell","header":{"controls":{"copy":{}}},"source":"curl -i -X GET \\\n  https://localhost/3013/peer/v1/id_token \\\n  -H 'Authorization: Bearer Albert'  // Albert is the default API key\n","lang":"shell"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here's an example of a decoded ID token:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"aud\": [],\n  \"exp\": 1704791985,  // expiration of the ID token\n  \"extra\": {  // additional cloud data automatically added to the token\n    \"actions\": {},\n    \"ids\": {\n      \"aws\": \"i-0e81f288c6bb1b46d\",\n      \"k8s\": \"8749c7a0-7898-4a81-9110-cb2cc4c46fab\"\n    },\n    \"tags\": [  // tags specified in the ticket\n      \"eyes\"\n    ]\n  },\n  \"iat\": 1704619185,  // time the token was issued\n  \"iss\": \"op1:mcpvj2mkrv9vxpmr3n8kp7ge\",  // identifies the issuer of the token\n  \"jti\": \"jwt1:-cscxhr62-g7p9q4mq6pcnbd\",\n  \"nbf\": 1704619185,\n  \"sub\": \"pr1:79mz7mx-grhncahygdd8d5-e\"  // identifies the workload (workload ID)\n}\n\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Below is a TypeScript example that fetches an ID token to send authenticated requests from your application. In this example, the application assumes the MIM Client is running on the same machine with the default port and API Key configuration (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/machine/run_multiple_clients"},"children":["Run multiple clients on the same machine"]}," for setting up a similar example)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"import axios from 'axios';\n\n// The API key should match the one used when running the MIM Client:\nconst MIM_API_KEY = \"Albert\"\n\nconst getJwt = async () => {\n    // Retrieve the Identity Token from the locally-run MIM Client, using the API key: \n    const response = await axios.get('http://localhost:3013/peer/v1/id_token', {\n      headers: {\n        Authorization: `Bearer ${MIM_API_KEY}`,\n      },\n    });\n  \n    // Extract the JWT from the response:\n    const jwt = response.data.jwt_id_token;\n  \n    return jwt;\n  };\n  \n\nasync function sendAuthenticatedRequest(url: string) {\n    // Configure authorization:\n  const jwt = await getJwt();\n  const headers = {\n    'Content-Type': 'application/json',\n    'Authorization': `Bearer ${jwt}`,\n  };\n\n  // Send the request:\n  const response = await axios.get(url, { headers });\n  return response;\n};\n\nsendAuthenticatedRequest('http://0.0.0.0:3333');\n","lang":"js"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-5-validate-id-tokens","__idx":7},"children":["Step 5: Validate ID tokens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The workload that receives the authenticated request can validate the ID token using the standard JWS method. This involves validating the signature of the ID token using the MIM IdP's public key, which is published via the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/operator/.well-known/jwks.json"]}," endpoint."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Below is an example of a Go server that validates such a request. In this example, the server only serves one route (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/protected"]},"), which returns a simple string."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"go","header":{"controls":{"copy":{}}},"source":"package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/labstack/echo/v4\"\n\tjwx \"github.com/lestrrat-go/echo-middleware-jwx\"\n\t\"github.com/lestrrat-go/jwx/jwk\"\n)\n\nfunc main() {\n\t// Replace <tenant> with the correct value:\n\tconst mimJwks = `http://<tenant>.mim.transmitsecurity.io/operator/.well-known/jwks.json`\n\n\t// create cancellable context:\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t// initiate a new server\n\te := echo.New()\n\n\t// create a new auto-refreshing JWK set:\n\tar := jwk.NewAutoRefresh(ctx)\n\tar.Configure(mimJwks, jwk.WithMinRefreshInterval(15*time.Minute))\n\tks, err := ar.Refresh(ctx, mimJwks)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"failed to refresh JWKS: %s\\n\", err))\n\t}\n\n\t// setup the middleware with the JWK set:\n\tjwxMiddleware := jwx.JWX(ks)\n\te.Use(jwxMiddleware)\n\n\t// serve a protected endpoint:\n\te.GET(\"/protected\", func(c echo.Context) error {\n\t\treturn c.String(http.StatusOK, \"Hello, fellow Workload!\")\n\t})\n\n\t// start the server:\n\te.Start(\":8000\")\n}\n","lang":"go"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":8},"children":["Next steps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Now that you've set up a basic workload authentication scenario, here are additional steps to consider:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#manage-workload-access"},"children":["Manage workload access"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#run-additional-clients"},"children":["Run other clients on the same machine"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#set-your-api-key"},"children":["Set a custom API key"]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"manage-workload-access","__idx":9},"children":["Manage workload access"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can add access controls that determine whether or not a workload can access the requested resource. These controls will be evaluated each time the MIM client interacts with the MIM IDP. Access controls are specified in the ticket itself (from ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Machine Identity"]}," > ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Access Control"]},"), but can be adjusted even after the ticket is used. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/machine/manage_access_controls"},"children":["Manage access controls"]}," for more."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"run-additional-clients","__idx":10},"children":["Run additional clients"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can run additional clients on the same machine, for example, to create separate identities for different workloads running on the same machine. Certain client configuration is required to make this work. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/machine/run_multiple_clients"},"children":["Run multiple clients on the same machine"]}," for more."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"set-your-api-key","__idx":11},"children":["Set your API key"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["While running, the Client allows other processes running on the workload to access its information via a locally-run API server (on port 3013 by default). Access to this server is limited to the same network adapter (127.0.0.1 by default, but can also be explicitly set) and is also protected by an API key. The default API key is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Albert"]},", but it can be configured using the environment variable ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["MIM_API_KEY"]},"."]}]},"headings":[{"value":"Quickstart: Workload authentication","id":"quickstart-workload-authentication","depth":1},{"value":"How it works","id":"how-it-works","depth":2},{"value":"Before you start","id":"before-you-start","depth":2},{"value":"Step 1: Download the client","id":"step-1-download-the-client","depth":2},{"value":"Step 2: Create a ticket","id":"step-2-create-a-ticket","depth":2},{"value":"Step 3: Run the client","id":"step-3-run-the-client","depth":2},{"value":"Step 4: Get ID tokens","id":"step-4-get-id-tokens","depth":2},{"value":"Step 5: Validate ID tokens","id":"step-5-validate-id-tokens","depth":2},{"value":"Next steps","id":"next-steps","depth":2},{"value":"Manage workload access","id":"manage-workload-access","depth":3},{"value":"Run additional clients","id":"run-additional-clients","depth":3},{"value":"Set your API key","id":"set-your-api-key","depth":3}],"frontmatter":{"title":"Quickstart","excludeFromSearch":true,"markdown":{"toc":{"depth":2}},"seo":{"title":"Quickstart: Workload authentication"}},"lastModified":"2025-11-23T08:46:19.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/machine/quick_start_jwt_auth","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}