API Authentication
ADA API använder JWT-baserad autentisering för /authenticates/api-code endpoint.
Snabbstart
- Skaffa din API-kod och registrera din publika nyckel med Visma Amili AB.
- Generera ett nyckelpar med en av de stödda algoritmerna (ES256, RS256, RS384, RS512, ES384, eller ES512).
- Skapa en JWT signerad med din privata nyckel.
- Autentisera och ta emot en access token.
- Använd access token i X-API-Key header för alla efterföljande API-requests.
- Uppdatera token vid behov.
GET /authenticates/api-code (JWT-baserad autentisering)
Syfte: Autentisera med en JWT (JSON Web Token) signerad med en privat nyckel.
Method: GET
Headers:
X-API-Key: JWT token som innehåller API-koden och utgångstid
Förutsättningar:
- Generera ett nyckelpar med en av de stödda algoritmerna
- Registrera din publika nyckel med Visma Amili AB
- Skaffa din API-kod från Visma Amili AB
Stödda algoritmer:
API:et stödjer följande JWT-signeringsalgoritmer:
- ES256 - ECDSA med P-256 kurva och SHA-256 hash
- ES384 - ECDSA med P-384 kurva och SHA-384 hash
- ES512 - ECDSA med P-521 kurva och SHA-512 hash
- RS256 - RSA-signatur med SHA-256 hash
- RS384 - RSA-signatur med SHA-384 hash
- RS512 - RSA-signatur med SHA-512 hash
Exempel på nyckelgenerering:
# Generera privat nyckel för ES256
openssl ecparam -name prime256v1 -genkey -noout -out jwt.private.ec.key
# Generera publik nyckel
openssl ec -in jwt.private.ec.key -pubout -out jwt.public.ec.key# Generera privat nyckel för ES384
openssl ecparam -name secp384r1 -genkey -noout -out jwt.private.ec.key
# Generera publik nyckel
openssl ec -in jwt.private.ec.key -pubout -out jwt.public.ec.key# Generera privat nyckel för ES512
openssl ecparam -name secp521r1 -genkey -noout -out jwt.private.ec.key
# Generera publik nyckel
openssl ec -in jwt.private.ec.key -pubout -out jwt.public.ec.key# Generera privat nyckel för RS256
openssl genrsa -out jwt.private.rsa.key 2048
# Generera publik nyckel
openssl rsa -in jwt.private.rsa.key -pubout -out jwt.public.rsa.keyTIP
Denna dokumentation använder ES256 i kodexemplen, men du kan använda vilken som helst av de stödda algoritmerna. Se till att använda samma algoritm när du signerar din JWT som den du registrerade med din publika nyckel.
JWT Token-struktur:
{
"api_code": "your-api-code",
"exp": "expiration-timestamp"
}Implementeringsdetaljer:
- JWT bör gå ut efter 10 minuter (sätt exp claim till UNIX timestamp)
- Kräver en privat nyckel som matchar en av de stödda algoritmerna
- JWT:en signeras med den privata nyckeln och skickas i
X-API-Keyheader - Algoritmen som används måste matcha den publika nyckel du registrerade med Visma Amili AB
Exempel från kod:
from datetime import datetime, timedelta
import jwt
exp = int((datetime.utcnow() + timedelta(minutes=10)).timestamp())
# Använd algoritmen som matchar din registrerade publika nyckel (ES256, RS256, etc.)
token = jwt.encode({"api_code": api_code, "exp": exp}, private_key, algorithm='ES256')
auth = requests.get(
url=f"{self.api_url}/authenticates/api-code",
headers={"X-API-Key": token}
)Response Format
Endpointen returnerar följande response-struktur vid lyckad autentisering:
{
"token": "access-token-for-subsequent-requests"
}Användning i API:et
Den returnerade access token används för alla efterföljande API-requests i X-API-Key header.
Exempel användning på POST /case-registrations:
auth = requests.post(
url=f"{api_url}/case--registrations",
headers={"X-API-Key": token},
json=case_data
)Token-utgång och uppdatering
- JWT tokens: Bör gå ut efter 10 minuter av säkerhetsskäl
- Access tokens (returnerade av API): Kan gå ut när som helst, klienter måste hantera utgång smidigt
- När access token går ut måste du autentisera igen för att få en ny token
Hantera token-utgång
Här är ett exempel på hur du hanterar token-utgång och uppdatering i din kod:
import axios, { AxiosError } from 'axios'
import jwt from 'jsonwebtoken'
import { readFileSync } from 'fs'
interface TokenInfo {
token: string
expiryTime: number // in milliseconds
}
class AuthTokenProvider {
private tokenInfo: TokenInfo | null = null
private readonly apiCode: string
private readonly privateKey: Buffer
constructor(apiCode: string, privateKeyPath: string) {
this.apiCode = apiCode
this.privateKey = readFileSync(privateKeyPath)
}
private async getNewAccessToken(): Promise<TokenInfo> {
// Create JWT with 10-minute expiry
const payload = {
api_code: this.apiCode,
exp: Math.floor(Date.now() / 1000) + 10 * 60,
}
// Använd algoritmen som matchar din registrerade publika nyckel
const jwt_token = jwt.sign(payload, this.privateKey, { algorithm: 'ES256' })
const response = await axios.get(
'https://api-sandbox.amili.se/authenticates/api-code',
{
headers: { 'X-API-Key': jwt_token },
}
)
const token = response.data.token
// Decode token to get expiry time
const decodedToken = jwt.decode(token)
if (!decodedToken || typeof decodedToken === 'string') {
throw new Error('Invalid token format received from server')
}
return {
token,
expiryTime: (decodedToken.exp || 0) * 1000, // Convert to milliseconds
}
}
async getValidToken(): Promise<string> {
// If we don't have a token or it's expiring soon, get a new one
if (
!this.tokenInfo ||
Date.now() + 5 * 60 * 1000 >= this.tokenInfo.expiryTime
) {
this.tokenInfo = await this.getNewAccessToken()
}
return this.tokenInfo.token
}
}
// Example usage:
async function makeApiCall() {
const auth = new AuthTokenProvider('your-api-code', 'path/to/private.key')
try {
// Get a valid token
const token = await auth.getValidToken()
// Use the token for your API call
const response = await axios.get(
'https://api-sandbox.amili.se/invoice/123',
{
headers: { 'X-API-Key': token },
}
)
return response.data
} catch (error) {
// Handle errors appropriately
console.error('API call failed:', error)
throw error
}
}import jwt
import requests
from datetime import datetime, timedelta
from dataclasses import dataclass
@dataclass
class TokenInfo:
token: str
expiry_time: int # in milliseconds
class AuthTokenProvider:
def __init__(self, api_code: str, private_key_path: str):
self.api_code = api_code
with open(private_key_path, 'r') as f:
self.private_key = f.read()
self.token_info: TokenInfo | None = None
def _get_new_access_token(self) -> TokenInfo:
# Create JWT with 10-minute expiry
exp = int((datetime.utcnow() + timedelta(minutes=10)).timestamp())
payload = {"api_code": self.api_code, "exp": exp}
# Använd algoritmen som matchar din registrerade publika nyckel
jwt_token = jwt.encode(payload, self.private_key, algorithm='ES256')
response = requests.get(
'https://api-sandbox.amili.se/authenticates/api-code',
headers={'X-API-Key': jwt_token}
)
response.raise_for_status()
token = response.json()['token']
# Decode token to get expiry time
decoded_token = jwt.decode(token, options={"verify_signature": False})
if not isinstance(decoded_token, dict):
raise ValueError('Invalid token format received from server')
return TokenInfo(
token=token,
expiry_time=int(decoded_token.get('exp', 0)) * 1000 # Convert to milliseconds
)
def get_valid_token(self) -> str:
# If we don't have a token or it's expiring soon, get a new one
if (not self.token_info or
int(datetime.now().timestamp() * 1000) + 5 * 60 * 1000 >= self.token_info.expiry_time):
self.token_info = self._get_new_access_token()
return self.token_info.token
# Example usage:
def make_api_call():
auth = AuthTokenProvider('your-api-code', 'path/to/private.key')
try:
# Get a valid token
token = auth.get_valid_token()
# Use the token for your API call
response = requests.get(
'https://api-sandbox.amili.se/invoice/123',
headers={'X-API-Key': token}
)
response.raise_for_status()
return response.json()
except Exception as e:
# Handle errors appropriately
print('API call failed:', e)
raiseusing System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Jose;
using Jose.Jws;
public class TokenInfo
{
public string Token { get; set; }
public long ExpiryTime { get; set; } // in milliseconds
}
public class AuthTokenProvider
{
private TokenInfo _tokenInfo;
private readonly string _apiCode;
private readonly string _privateKey;
public AuthTokenProvider(string apiCode, string privateKeyPath)
{
_apiCode = apiCode;
_privateKey = File.ReadAllText(privateKeyPath);
}
private async Task<TokenInfo> GetNewAccessTokenAsync()
{
// Create JWT with 10-minute expiry
var payload = new
{
api_code = _apiCode,
exp = DateTimeOffset.UtcNow.AddMinutes(10).ToUnixTimeSeconds()
};
// Använd algoritmen som matchar din registrerade publika nyckel
var jwtToken = JWT.Encode(payload, _privateKey, JwsAlgorithm.ES256);
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("X-API-Key", jwtToken);
var response = await httpClient.GetAsync("https://api-sandbox.amili.se/authenticates/api-code");
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
var responseData = JsonSerializer.Deserialize<JsonElement>(responseContent);
var token = responseData.GetProperty("token").GetString();
// Decode token to get expiry time
var decodedToken = JWT.Decode(token);
var tokenData = JsonSerializer.Deserialize<JsonElement>(decodedToken);
if (!tokenData.TryGetProperty("exp", out var expProperty))
{
throw new InvalidOperationException("Invalid token format received from server");
}
return new TokenInfo
{
Token = token,
ExpiryTime = expProperty.GetInt64() * 1000 // Convert to milliseconds
};
}
public async Task<string> GetValidTokenAsync()
{
// If we don't have a token or it's expiring soon, get a new one
if (_tokenInfo == null ||
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + 5 * 60 * 1000 >= _tokenInfo.ExpiryTime)
{
_tokenInfo = await GetNewAccessTokenAsync();
}
return _tokenInfo.Token;
}
}
// Example usage:
public static async Task<object> MakeApiCallAsync()
{
var auth = new AuthTokenProvider("your-api-code", "path/to/private.key");
try
{
// Get a valid token
var token = await auth.GetValidTokenAsync();
// Use the token for your API call
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("X-API-Key", token);
var response = await httpClient.GetAsync("https://api-sandbox.amili.se/invoice/123");
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<object>(responseContent);
}
catch (Exception ex)
{
// Handle errors appropriately
Console.WriteLine($"API call failed: {ex.Message}");
throw;
}
}Säkerhetsöverväganden
- Använd alltid HTTPS för all API-kommunikation
- Håll din privata nyckel säker och exponera den aldrig i client-side kod
- Använd starka nyckelstorlekar (minimum 2048 bitar för RSA, P-256 eller högre för ECDSA)
- Lagra privata nycklar säkert med nyckelhanteringssystem eller krypterad lagring
