> ## Documentation Index
> Fetch the complete documentation index at: https://developer.utgl.io/llms.txt
> Use this file to discover all available pages before exploring further.

# How to authenticate with the Issuing API using Go

## Overview

This tutorial shows you how to authenticate to the Issuing API using Go by generating signed JSON Web Tokens (JWTs).

## In This Recipe

1. Install Dependencies
2. Create signJWT function
3. Usage - GET Request
4. Usage - POST Request

## Prerequisites

* Go 1.18 or higher
* Your Issuing API credentials (Access Key and RSA key pair)

## Step 1: Install Dependencies

Install the required Go modules:

```bash theme={null}
go get github.com/golang-jwt/jwt/v5
```

## Step 2: Create signJWT Function

Create a utility function to handle JWT signing with SHA256 body hashing:

```go theme={null}
package main

import (
	"crypto"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"os"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

// SignJWT creates a signed JWT for Issuing API authentication
func SignJWT(privateKeyPath, accessKey, uri, method, requestBody string) (string, error) {
	// Read private key file
	keyData, err := os.ReadFile(privateKeyPath)
	if err != nil {
		return "", fmt.Errorf("failed to read private key: %w", err)
	}

	// Parse PEM block
	block, _ := pem.Decode(keyData)
	if block == nil {
		return "", fmt.Errorf("failed to parse PEM block")
	}

	// Parse private key
	var privateKey *rsa.PrivateKey
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		// Try PKCS1 format as fallback
		privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return "", fmt.Errorf("failed to parse private key: %w", err)
		}
	} else {
		var ok bool
		privateKey, ok = key.(*rsa.PrivateKey)
		if !ok {
			return "", fmt.Errorf("private key is not RSA")
		}
	}

	// Hash request body with SHA256
	hash := sha256.Sum256([]byte(requestBody))
	hashedBody := hex.EncodeToString(hash[:])

	now := time.Now()

	// Create JWT claims
	claims := jwt.MapClaims{
		"sub":    accessKey,
		"iat":    now.Unix(),
		"exp":    now.Add(30 * time.Second).Unix(), // 30 second expiration
		"uri":    uri,
		"method": method,
		"body":   hashedBody,
	}

	// Create and sign token
	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	token.Header["typ"] = "JWT"

	signedToken, err := token.SignedString(privateKey)
	if err != nil {
		return "", fmt.Errorf("failed to sign token: %w", err)
	}

	return signedToken, nil
}
```

### Important Notes:

* **Request body is hashed** with SHA256 before signing
* **30 second expiration** for security
* **Private key format**: PEM-encoded PKCS8 or PKCS1
* **Empty body**: Use empty string `""` for GET requests

## Step 3: Usage - GET Request

Make a simple GET request to test connectivity:

```go theme={null}
package main

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

func main() {
	privateKeyPath := "./utgl-access.private" // Path to your private key
	accessKey := "6e33a078-99ed-4aa1-8e67-b0e19e9475fd" // Replace with your access key

	uri := "/v1/ping"
	method := "GET"
	requestBody := ""

	// Generate JWT
	jwtToken, err := SignJWT(privateKeyPath, accessKey, uri, method, requestBody)
	if err != nil {
		fmt.Printf("Error signing JWT: %v\n", err)
		return
	}

	// Make HTTP request
	client := &http.Client{}
	req, err := http.NewRequest(method, "https://sandbox.access.utgl.io"+uri, nil)
	if err != nil {
		fmt.Printf("Error creating request: %v\n", err)
		return
	}

	req.Header.Set("Authorization", "Bearer "+jwtToken)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("Error making request: %v\n", err)
		return
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Printf("%s %s %d\n", method, uri, resp.StatusCode)
	fmt.Printf("Response: %s\n", string(body))
}
```

## Step 4: Usage - POST Request

Make a POST request with a request body:

```go theme={null}
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	privateKeyPath := "./utgl-access.private" // Path to your private key
	accessKey := "6e33a078-99ed-4aa1-8e67-b0e19e9475fd" // Replace with your access key

	uri := "/v1/ping"
	method := "POST"
	requestBody := `{"message": "Hello, world!"}`

	// Generate JWT
	jwtToken, err := SignJWT(privateKeyPath, accessKey, uri, method, requestBody)
	if err != nil {
		fmt.Printf("Error signing JWT: %v\n", err)
		return
	}

	// Make HTTP request
	client := &http.Client{}
	req, err := http.NewRequest(method, "https://sandbox.access.utgl.io"+uri, bytes.NewBufferString(requestBody))
	if err != nil {
		fmt.Printf("Error creating request: %v\n", err)
		return
	}

	req.Header.Set("Authorization", "Bearer "+jwtToken)
	req.Header.Set("Content-Type", "application/json")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("Error making request: %v\n", err)
		return
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Printf("%s %s %d\n", method, uri, resp.StatusCode)
	fmt.Printf("Response: %s\n", string(body))
}
```

## Common Pitfalls

<Warning>
  **Body Hashing is Critical!** The `SignJWT` function automatically hashes the request body with SHA256 before including it in the JWT claims. Make sure you pass the **exact same request body** to both `SignJWT()` and your HTTP request.
</Warning>

<Tip>
  **Token Expiration**: JWT tokens expire after 30 seconds. Generate a fresh token for each API request to avoid authentication errors.
</Tip>

## Next Steps

* See [Authentication](/issuing/getting-started/request-signing) for more details on JWT signing
* Explore [API Endpoints](/api-reference) to see what you can do
* Check out [Request Signing](/issuing/getting-started/request-signing) for security best practices
