API Authentication
ADA API tillhandahåller två autentiseringsmetoder för /authenticates/api-code endpoint
Snabbstart
- Skaffa din API-kod och registrera din publika nyckel (för GET-metod) eller statisk IP (för POST-metod) med Visma Amili AB.
- Välj autentiseringsmetod:
- GET: Använd JWT signerad med din privata nyckel.
- POST: Använd din API-kod i request body (från en registrerad statisk IP).
- 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 EC-nyckelpar (ES256-algoritm)
- Registrera din publika nyckel med Visma Amili AB
- Skaffa din API-kod från Visma Amili AB
Nyckelgenerering:
bash
# Generate private key
openssl ecparam -name prime256v1 -genkey -noout -out jwt.private.ec.key
# Generate public key
openssl ec -in jwt.private.ec.key -pubout -out jwt.public.ec.keyJWT Token-struktur:
json
{
"api_code": "your-api-code",
"exp": "expiration-timestamp"
}Implementeringsdetaljer:
- Använder ES512-algoritm för JWT-signering
- JWT bör gå ut efter 10 minuter (sätt exp claim till UNIX timestamp)
- Kräver en privat nyckel (EC-nyckelpar)
- JWT:en signeras med den privata nyckeln och skickas i
X-API-Keyheader
Exempel från kod:
python
from datetime import datetime, timedelta
import jwt
exp = int((datetime.utcnow() + timedelta(minutes=10)).timestamp())
token = jwt.encode({"api_code": api_code, "exp": exp}, private_key, algorithm='ES512')
auth = requests.get(
url=f"{self.api_url}/authenticates/api-code",
headers={"X-API-Key": token}
)POST /authenticates/api-code (Enkel API-kod autentisering)
Syfte: Autentisera med en enkel API-kod utan JWT.
Method: POST
Content-Type: application/json
Förutsättningar:
- Registrera din statiska IP-adress med Visma Amili AB
- Skaffa din API-kod från Visma Amili AB
Request Body:
json
{
"code": "your-api-code"
}Implementeringsdetaljer:
- Enklare autentiseringsmetod
- Ingen privat nyckel krävs
- Direkt API-kod inlämning i JSON body
- Kräver att requests kommer från en registrerad statisk IP-adress
Exempel från kod:
python
auth = requests.post(
url=f"{self.api_url}/authenticates/api-code",
json={"code": self.api_code}
)Response Format
Båda endpoints returnerar samma response-struktur vid lyckad autentisering:
json
{
"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:
python
auth = requests.post(
url=f"{api_url}/case--registrations",
headers={"X-API-Key": token},
json=case_data
)Token-utgång och uppdatering
- JWT tokens (GET-metod): 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:
typescript
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,
}
const jwt_token = jwt.sign(payload, this.privateKey, { algorithm: 'ES512' })
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
}
}python
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}
jwt_token = jwt.encode(payload, self.private_key, algorithm='ES512')
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)
raisecsharp
using 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()
};
var jwtToken = JWT.Encode(payload, _privateKey, JwsAlgorithm.ES512);
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
- GET-metod (JWT): Mer säker eftersom den använder kryptografiska signaturer
- POST-metod: Kräver statisk IP-registrering för säkerhet
- Använd alltid HTTPS för all API-kommunikation
- Håll din privata nyckel säker och exponera den aldrig i client-side kod
