mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 12:05:07 +00:00
2955 lines
90 KiB
HTML
2955 lines
90 KiB
HTML
|
|
<!doctype html>
|
|
<html lang="en" class="no-js">
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
|
|
<meta name="description" content="Movie, TV, and Music archival software. User guide and REST API reference">
|
|
|
|
|
|
|
|
<link rel="canonical" href="https://docs.unshackle.dev/dev/rest-api/authentication/">
|
|
|
|
|
|
<link rel="prev" href="../quickstart/">
|
|
|
|
|
|
<link rel="next" href="../endpoints/">
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="icon" href="../../../assets/favicon.svg">
|
|
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.8.0">
|
|
|
|
|
|
|
|
<title>Authentication - unshackle</title>
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../../../assets/stylesheets/main.57a0d79d.min.css">
|
|
|
|
|
|
<link rel="stylesheet" href="../../../assets/stylesheets/palette.fb2fffe2.min.css">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
|
|
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="../../../assets/_mkdocstrings.css">
|
|
|
|
<link rel="stylesheet" href="../../../assets/extra.css">
|
|
|
|
<script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
|
|
|
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo">
|
|
|
|
|
|
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
|
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
|
<label class="md-overlay" for="__drawer"></label>
|
|
<div data-md-component="skip">
|
|
|
|
|
|
<a href="#rest-api-authentication" class="md-skip">
|
|
Skip to content
|
|
</a>
|
|
|
|
</div>
|
|
<div data-md-component="announce">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<header class="md-header" data-md-component="header">
|
|
<nav class="md-header__inner md-grid" aria-label="Header">
|
|
<a href="../../.." title="unshackle" class="md-header__button md-logo" aria-label="unshackle" data-md-component="logo">
|
|
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m800-274-80-80v-206H514l-80-80h166v-80q0-50-34.5-85T481-840q-50 0-84 34.5T363-720v9l-73-73q22-61 75-98.5T481-920q83 0 141 58.5T680-720v80h40q33 0 56.5 23.5T800-560v286Zm20 246-62-62q-11 5-20 7.5T720-80H240q-33 0-56.5-23.5T160-160v-400q0-25 14.5-46t37.5-30L28-820l56-56L876-84l-56 56ZM686-160 539-309q-11 11-25.5 17t-31.5 6q-33 0-56.5-23.5T402-366q0-17 6-31.5t17-25.5L286-560h-46v400h446ZM486-360Zm131-97Z"/></svg>
|
|
|
|
</a>
|
|
<label class="md-header__button md-icon" for="__drawer">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
|
</label>
|
|
<div class="md-header__title" data-md-component="header-title">
|
|
<div class="md-header__ellipsis">
|
|
<div class="md-header__topic">
|
|
<span class="md-ellipsis">
|
|
unshackle
|
|
</span>
|
|
</div>
|
|
<div class="md-header__topic" data-md-component="header-topic">
|
|
<span class="md-ellipsis">
|
|
|
|
Authentication
|
|
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<form class="md-header__option" data-md-component="palette">
|
|
|
|
|
|
|
|
|
|
<input class="md-option" data-md-color-media="(prefers-color-scheme)" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
|
|
|
|
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1M8 13h8v-2H8zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4a5 5 0 0 0 5-5 5 5 0 0 0-5-5"/></svg>
|
|
</label>
|
|
|
|
|
|
|
|
|
|
|
|
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
|
|
|
|
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5m0 8a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3"/></svg>
|
|
</label>
|
|
|
|
|
|
|
|
|
|
|
|
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to system preference" type="radio" name="__palette" id="__palette_2">
|
|
|
|
<label class="md-header__button md-icon" title="Switch to system preference" for="__palette_0" hidden>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3"/></svg>
|
|
</label>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-header__button md-icon" for="__search">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
|
</label>
|
|
<div class="md-search" data-md-component="search" role="dialog">
|
|
<label class="md-search__overlay" for="__search"></label>
|
|
<div class="md-search__inner" role="search">
|
|
<form class="md-search__form" name="search">
|
|
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
|
<label class="md-search__icon md-icon" for="__search">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
|
</label>
|
|
<nav class="md-search__options" aria-label="Search">
|
|
|
|
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
|
|
</a>
|
|
|
|
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
|
</button>
|
|
</nav>
|
|
|
|
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
|
|
|
</form>
|
|
<div class="md-search__output">
|
|
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
|
<div class="md-search-result" data-md-component="search-result">
|
|
<div class="md-search-result__meta">
|
|
Initializing search
|
|
</div>
|
|
<ol class="md-search-result__list" role="presentation"></ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-header__source">
|
|
<a href="https://github.com/unshackle-dl/unshackle" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
unshackle-dl/unshackle
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
</header>
|
|
|
|
<div class="md-container" data-md-component="container">
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
|
<div class="md-grid">
|
|
<ul class="md-tabs__list">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../.." class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
Home
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../../getting-started/installation/" class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
Getting Started
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../../guide/downloading/" class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
User Guide
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../../reference/configuration/" class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
Configuration
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item md-tabs__item--active">
|
|
<a href="../../creating-a-service/" class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
Developer
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-tabs__item">
|
|
<a href="../../../reference/api/service/" class="md-tabs__link">
|
|
|
|
|
|
|
|
|
|
|
|
API Reference
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
|
|
<main class="md-main" data-md-component="main">
|
|
<div class="md-main__inner md-grid">
|
|
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
|
|
<label class="md-nav__title" for="__drawer">
|
|
<a href="../../.." title="unshackle" class="md-nav__button md-logo" aria-label="unshackle" data-md-component="logo">
|
|
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m800-274-80-80v-206H514l-80-80h166v-80q0-50-34.5-85T481-840q-50 0-84 34.5T363-720v9l-73-73q22-61 75-98.5T481-920q83 0 141 58.5T680-720v80h40q33 0 56.5 23.5T800-560v286Zm20 246-62-62q-11 5-20 7.5T720-80H240q-33 0-56.5-23.5T160-160v-400q0-25 14.5-46t37.5-30L28-820l56-56L876-84l-56 56ZM686-160 539-309q-11 11-25.5 17t-31.5 6q-33 0-56.5-23.5T402-366q0-17 6-31.5t17-25.5L286-560h-46v400h446ZM486-360Zm131-97Z"/></svg>
|
|
|
|
</a>
|
|
unshackle
|
|
</label>
|
|
|
|
<div class="md-nav__source">
|
|
<a href="https://github.com/unshackle-dl/unshackle" title="Go to repository" class="md-source" data-md-component="source">
|
|
<div class="md-source__icon md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
|
|
</div>
|
|
<div class="md-source__repository">
|
|
unshackle-dl/unshackle
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../.." class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Home
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Getting Started
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_2">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Getting Started
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../getting-started/installation/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Installation
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../getting-started/quickstart/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Quick Start
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../getting-started/configuration-file/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
The Configuration File
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
User Guide
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_3">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
User Guide
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/downloading/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Downloading
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/cli-reference/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
CLI Reference
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/drm-and-cdm/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
DRM & CDM Setup
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/proxies-and-vpn/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Proxies & VPN
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/vaults/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Key Vaults
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/output-and-naming/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Output & Naming
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/subtitles/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Subtitles
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../guide/troubleshooting/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Troubleshooting
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
|
|
|
|
|
|
<div class="md-nav__link md-nav__container">
|
|
<a href="../../../reference/configuration/" class="md-nav__link ">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Configuration
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<label class="md-nav__link " for="__nav_4" id="__nav_4_label" tabindex="0">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_4">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Configuration
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/directories/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Directories
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/services/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Services & Authentication
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/download/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Download & Processing
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/output/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Output & Naming
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/drm/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
DRM & CDM
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/network/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Network & Proxy
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/vaults/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Key Vaults
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/post-scripts/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Post-Scripts
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/misc/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Other Settings
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/configuration/example/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Annotated Example
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" checked>
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Developer
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="true">
|
|
<label class="md-nav__title" for="__nav_5">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
Developer
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../creating-a-service/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Creating a Service
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../service-architecture/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Service Architecture
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_3" checked>
|
|
|
|
|
|
<div class="md-nav__link md-nav__container">
|
|
<a href="../" class="md-nav__link ">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
REST API
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<label class="md-nav__link " for="__nav_5_3" id="__nav_5_3_label" tabindex="">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
</div>
|
|
|
|
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_3_label" aria-expanded="true">
|
|
<label class="md-nav__title" for="__nav_5_3">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
REST API
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../quickstart/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Quick Start
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active">
|
|
|
|
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__link md-nav__link--active" for="__toc">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Authentication
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<a href="./" class="md-nav__link md-nav__link--active">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Authentication
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="__toc">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
Table of contents
|
|
</label>
|
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#the-short-version" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
The short version
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#configuring-authentication" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Configuring authentication
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Configuring authentication">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#a-single-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
A single API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#multiple-keys-with-users" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Multiple keys with users
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cdm-device-access-per-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CDM device access per API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#disabling-authentication" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Disabling authentication
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#how-clients-present-credentials" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
How clients present credentials
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="How clients present credentials">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#the-health-check-needs-an-api-key-too" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
The health check needs an API key too
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#authentication-failures" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Authentication failures
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#rate-limit-rejections" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Rate limit rejections
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#access-controls-on-top-of-the-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Access controls on top of the API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Access controls on top of the API key">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#per-key-service-allowlists" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Per-key service allowlists
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cdm-overrides-and-per-job-credentials" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CDM overrides and per-job credentials
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#session-ip-binding-remote-sessions" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Session IP binding (remote sessions)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#transport-security-https" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Transport security (HTTPS)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#quick-reference" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Quick reference
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#developer-notes-how-the-server-does-the-check" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Developer notes: how the server does the check
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Developer notes: how the server does the check">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#two-middlewares-one-header" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Two middlewares, one header
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cors-and-the-authorization-header" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CORS and the Authorization header
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#where-device-access-is-read" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Where device access is read
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../endpoints/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Endpoints
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../job-lifecycle/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Job Lifecycle
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../dashboard/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Dashboard
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../remote-sessions/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Remote Sessions
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../errors/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Error Responses
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
|
|
|
|
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
|
|
|
|
|
|
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
API Reference
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="md-nav__icon md-icon"></span>
|
|
</label>
|
|
|
|
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
|
|
<label class="md-nav__title" for="__nav_6">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
|
|
|
|
API Reference
|
|
|
|
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/api/service/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Service
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/api/config/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Config
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/api/tracks/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Tracks
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/api/titles/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
Titles
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../../reference/api/drm/" class="md-nav__link">
|
|
|
|
|
|
|
|
<span class="md-ellipsis">
|
|
|
|
|
|
DRM
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="__toc">
|
|
<span class="md-nav__icon md-icon"></span>
|
|
Table of contents
|
|
</label>
|
|
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#the-short-version" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
The short version
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#configuring-authentication" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Configuring authentication
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Configuring authentication">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#a-single-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
A single API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#multiple-keys-with-users" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Multiple keys with users
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cdm-device-access-per-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CDM device access per API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#disabling-authentication" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Disabling authentication
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#how-clients-present-credentials" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
How clients present credentials
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="How clients present credentials">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#the-health-check-needs-an-api-key-too" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
The health check needs an API key too
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#authentication-failures" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Authentication failures
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#rate-limit-rejections" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Rate limit rejections
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#access-controls-on-top-of-the-api-key" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Access controls on top of the API key
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Access controls on top of the API key">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#per-key-service-allowlists" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Per-key service allowlists
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cdm-overrides-and-per-job-credentials" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CDM overrides and per-job credentials
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#session-ip-binding-remote-sessions" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Session IP binding (remote sessions)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#transport-security-https" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Transport security (HTTPS)
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#quick-reference" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Quick reference
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#developer-notes-how-the-server-does-the-check" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Developer notes: how the server does the check
|
|
|
|
</span>
|
|
</a>
|
|
|
|
<nav class="md-nav" aria-label="Developer notes: how the server does the check">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#two-middlewares-one-header" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Two middlewares, one header
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#cors-and-the-authorization-header" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
CORS and the Authorization header
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#where-device-access-is-read" class="md-nav__link">
|
|
<span class="md-ellipsis">
|
|
|
|
Where device access is read
|
|
|
|
</span>
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="md-content" data-md-component="content">
|
|
|
|
<article class="md-content__inner md-typeset">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<h1 id="rest-api-authentication">REST API Authentication<a class="headerlink" href="#rest-api-authentication" title="Permanent link">¶</a></h1>
|
|
<p>The unshackle REST API protects every endpoint (with one exception) behind a single API key. Clients present that API key in a request header, and the server examines it against the API keys in your configuration. This page explains how that check works, how to configure API keys, and how clients send them. It also explains the extra controls on top of the API key: per-key service allowlists, per-key CDM device access, and remote session IP binding.</p>
|
|
<p>You start the API with the <a href="../../../guide/cli-reference/"><code>unshackle serve</code></a> command. Authentication is on by default: if you start the server without an API key configured, it refuses to launch.</p>
|
|
<div class="admonition note">
|
|
<p class="admonition-title">Which server am I running?</p>
|
|
<p><code>unshackle serve</code> can operate in two shapes, and each uses a slightly different piece of code to examine your API key:</p>
|
|
<ul>
|
|
<li>The <strong>integrated server</strong> (the default) exposes the REST API <em>and</em> the pywidevine / pyplayready CDM HTTP endpoints.</li>
|
|
<li>The <strong>API-only server</strong> (<code>--api-only</code>, also implied by <code>--remote-only</code>) exposes only the REST API.</li>
|
|
</ul>
|
|
<p>The header, the config, and the behaviour you see as a client are the same in both. The differences are internal, and the developer notes below give them.</p>
|
|
</div>
|
|
<h2 id="the-short-version">The short version<a class="headerlink" href="#the-short-version" title="Permanent link">¶</a></h2>
|
|
<ol>
|
|
<li>Add an <code>api_secret</code> to the <code>serve</code> section of your <code>unshackle.yaml</code>.</li>
|
|
<li>Start the server with <code>unshackle serve</code>.</li>
|
|
<li>Send that secret in the <code>X-Secret-Key</code> header on every request, <code>GET /api/health</code> included.</li>
|
|
</ol>
|
|
<div class="language-bash highlight"><span class="filename">A minimal authenticated request</span><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-Secret-Key: your-secret-key-here"</span><span class="w"> </span><span class="se">\</span>
|
|
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="w"> </span>http://127.0.0.1:8786/api/services
|
|
</span></code></pre></div>
|
|
<h2 id="configuring-authentication">Configuring authentication<a class="headerlink" href="#configuring-authentication" title="Permanent link">¶</a></h2>
|
|
<p>All authentication settings live under the <code>serve</code> section of your unshackle config file (<code>unshackle.yaml</code>).</p>
|
|
<h3 id="a-single-api-key">A single API key<a class="headerlink" href="#a-single-api-key" title="Permanent link">¶</a></h3>
|
|
<p>The simplest setup defines one shared secret with <code>api_secret</code>:</p>
|
|
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="nt">serve</span><span class="p">:</span>
|
|
</span><span id="__span-1-2"><a id="__codelineno-1-2" name="__codelineno-1-2" href="#__codelineno-1-2"></a><span class="w"> </span><span class="nt">api_secret</span><span class="p">:</span><span class="w"> </span><span class="s">"a-long-random-secret-string"</span>
|
|
</span></code></pre></div>
|
|
<p>This value becomes the one and only API key that the server accepts. The server allows requests that present it in the <code>X-Secret-Key</code> header. The server rejects everything else.</p>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title">The server will not start without a key</p>
|
|
<p>If you start <code>unshackle serve</code> with no <code>api_secret</code> configured, and you have <em>not</em> passed <code>--no-key</code>, the command fails immediately with:</p>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>No 'api_secret' in the 'serve' section of your config
|
|
</span></code></pre></div>
|
|
<p>Choose a long, unpredictable value. The API key is the only thing standing between the internet and your CDM and download endpoints.</p>
|
|
</div>
|
|
<h3 id="multiple-keys-with-users">Multiple keys with <code>users</code><a class="headerlink" href="#multiple-keys-with-users" title="Permanent link">¶</a></h3>
|
|
<p>For finer control you can define named users, each with their own API key. A user entry can also grant CDM device access and restrict which services that API key may touch:</p>
|
|
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="nt">serve</span><span class="p">:</span>
|
|
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="w"> </span><span class="nt">api_secret</span><span class="p">:</span><span class="w"> </span><span class="s">"the-primary-admin-key"</span>
|
|
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="w"> </span><span class="nt">users</span><span class="p">:</span>
|
|
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="w"> </span><span class="nt">a-secret-key-for-alice</span><span class="p">:</span>
|
|
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">alice</span>
|
|
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="w"> </span><span class="nt">devices</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"my_widevine_device"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># Widevine (.wvd) devices this key may use</span>
|
|
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="w"> </span><span class="nt">playready_devices</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"my_playready_prd"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># PlayReady (.prd) devices this key may use</span>
|
|
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="w"> </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"EXAMPLE1"</span><span class="p p-Indicator">,</span><span class="w"> </span><span class="s">"EXAMPLE2"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># optional per-key service allowlist</span>
|
|
</span><span id="__span-3-9"><a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="w"> </span><span class="nt">a-secret-key-for-bob</span><span class="p">:</span>
|
|
</span><span id="__span-3-10"><a id="__codelineno-3-10" name="__codelineno-3-10" href="#__codelineno-3-10"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bob</span>
|
|
</span><span id="__span-3-11"><a id="__codelineno-3-11" name="__codelineno-3-11" href="#__codelineno-3-11"></a><span class="w"> </span><span class="nt">devices</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"my_widevine_device"</span><span class="p p-Indicator">]</span>
|
|
</span></code></pre></div>
|
|
<p>The config key of each entry under <code>users</code> (for example <code>a-secret-key-for-alice</code>) <em>is</em> the secret that client sends in <code>X-Secret-Key</code>. The <code>username</code> is a human-readable label used in logs and in job/history metadata.</p>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">How <code>api_secret</code> and <code>users</code> combine (integrated server)</p>
|
|
<p>On the integrated server, the server automatically adds the configured <code>api_secret</code> as an extra accepted API key alongside everyone in <code>users</code>. It gives that API key access to all loaded Widevine and PlayReady devices, and labels it <code>api_user</code>. So <code>api_secret</code> behaves as an admin-level API key in addition to any named users you define.</p>
|
|
<p>On the API-only server, the server accepts only <code>api_secret</code>: the single-key model. (See the developer notes for the exact difference.)</p>
|
|
</div>
|
|
<h3 id="cdm-device-access-per-api-key">CDM device access per API key<a class="headerlink" href="#cdm-device-access-per-api-key" title="Permanent link">¶</a></h3>
|
|
<p>Under the integrated server, an API key can do DRM licensing only through a CDM device that its user entry lists:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Config key</th>
|
|
<th>Grants access to</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>devices</code></td>
|
|
<td>Widevine <code>.wvd</code> devices, by name</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>playready_devices</code></td>
|
|
<td>PlayReady <code>.prd</code> devices, by name</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title">PlayReady access is opt-in per user</p>
|
|
<p>If a user entry omits <code>playready_devices</code>, the server gives that API key <strong>no</strong> PlayReady access, and logs a warning at startup. You must name the PlayReady devices explicitly for any API key that needs them. Widevine and PlayReady device files themselves are auto-loaded from your WVDs and PRDs directories.</p>
|
|
</div>
|
|
<div class="admonition note">
|
|
<p class="admonition-title">Grants decide whether the server returns content keys</p>
|
|
<p>The devices on an API key decide which CDM the server can use. Two grants in the same user entry decide whether the server returns content keys (<code>KID:KEY</code>) for a remote session. <code>server_cdm</code> lets the server license with those devices. <code>server_vault</code> lets a remote session that the client's own device licenses read the server vault, and the server then makes no live licence. Without either grant, the server can only proxy CDM challenges, so the client must operate its own CDM. See <a href="../../../reference/configuration/services/"><code>serve</code></a> for both grants.</p>
|
|
</div>
|
|
<h3 id="disabling-authentication">Disabling authentication<a class="headerlink" href="#disabling-authentication" title="Permanent link">¶</a></h3>
|
|
<p>The <code>--no-key</code> flag turns authentication off entirely. The server allows every request, ignores <code>X-Secret-Key</code>, and needs no API key in your configuration.</p>
|
|
<div class="language-bash highlight"><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>unshackle<span class="w"> </span>serve<span class="w"> </span>--no-key
|
|
</span></code></pre></div>
|
|
<div class="admonition danger">
|
|
<p class="admonition-title">Only use <code>--no-key</code> on a trusted, isolated network</p>
|
|
<p>With <code>--no-key</code>, any client that can reach the port can show your services, start downloads, and use your CDM devices. Never put a <code>--no-key</code> server on an untrusted network.</p>
|
|
</div>
|
|
<h2 id="how-clients-present-credentials">How clients present credentials<a class="headerlink" href="#how-clients-present-credentials" title="Permanent link">¶</a></h2>
|
|
<p>Clients authenticate by sending the secret in a single HTTP request header on every call:</p>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>X-Secret-Key: <your-secret>
|
|
</span></code></pre></div>
|
|
<p>The value must match one of the configured API keys exactly (<code>api_secret</code>, or one of the keys under <code>users</code>). There is no login step, no HTTP session cookie, and no token exchange for API access. The client sends the same static API key on each request.</p>
|
|
<div class="admonition info">
|
|
<p class="admonition-title">One route also accepts the key as a query parameter</p>
|
|
<p>The job event stream, <code>GET /api/download/jobs/{job_id}/events</code>, also accepts the API key in a <code>secret_key</code> query parameter. This is for browsers: the <code>EventSource</code> API cannot set request headers. The exception applies to that one route only, and it works in both server modes.</p>
|
|
<div class="language-javascript highlight"><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="ow">new</span><span class="w"> </span><span class="nx">EventSource</span><span class="p">(</span><span class="sb">`http://127.0.0.1:8786/api/download/jobs/</span><span class="si">${</span><span class="nx">jobId</span><span class="si">}</span><span class="sb">/events?secret_key=</span><span class="si">${</span><span class="nx">key</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
|
</span></code></pre></div>
|
|
<p>The header always wins. If the request has an <code>X-Secret-Key</code> header, the server examines that header and ignores the query parameter. The server rejects a wrong header even when the query parameter holds a valid API key.</p>
|
|
<p>An API key in a URL is easier to leak than an API key in a header, because proxies and browser history can record it. Use the header wherever your client can set one.</p>
|
|
</div>
|
|
<div class="tabbed-set tabbed-alternate" data-tabs="1:3"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">curl</label><label for="__tabbed_1_2">Python (requests)</label><label for="__tabbed_1_3">JavaScript (fetch)</label></div>
|
|
<div class="tabbed-content">
|
|
<div class="tabbed-block">
|
|
<div class="language-bash highlight"><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-Secret-Key: your-secret-key-here"</span><span class="w"> </span><span class="se">\</span>
|
|
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="w"> </span>http://127.0.0.1:8786/api/services
|
|
</span></code></pre></div>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<div class="language-python highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="kn">import</span><span class="w"> </span><span class="nn">requests</span>
|
|
</span><span id="__span-8-2"><a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a>
|
|
</span><span id="__span-8-3"><a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="n">BASE</span> <span class="o">=</span> <span class="s2">"http://127.0.0.1:8786"</span>
|
|
</span><span id="__span-8-4"><a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a><span class="n">HEADERS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"X-Secret-Key"</span><span class="p">:</span> <span class="s2">"your-secret-key-here"</span><span class="p">}</span>
|
|
</span><span id="__span-8-5"><a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a>
|
|
</span><span id="__span-8-6"><a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">BASE</span><span class="si">}</span><span class="s2">/api/services"</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">HEADERS</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span>
|
|
</span><span id="__span-8-7"><a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="n">resp</span><span class="o">.</span><span class="n">raise_for_status</span><span class="p">()</span>
|
|
</span><span id="__span-8-8"><a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="nb">print</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">json</span><span class="p">())</span>
|
|
</span></code></pre></div>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<div class="language-javascript highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="kd">const</span><span class="w"> </span><span class="nx">resp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">await</span><span class="w"> </span><span class="nx">fetch</span><span class="p">(</span><span class="s2">"http://127.0.0.1:8786/api/services"</span><span class="p">,</span><span class="w"> </span><span class="p">{</span>
|
|
</span><span id="__span-9-2"><a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a><span class="w"> </span><span class="nx">headers</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="s2">"X-Secret-Key"</span><span class="o">:</span><span class="w"> </span><span class="s2">"your-secret-key-here"</span><span class="w"> </span><span class="p">},</span>
|
|
</span><span id="__span-9-3"><a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="p">});</span>
|
|
</span><span id="__span-9-4"><a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">resp</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span><span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">Error</span><span class="p">(</span><span class="sb">`Server returned </span><span class="si">${</span><span class="nx">resp</span><span class="p">.</span><span class="nx">status</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
|
|
</span><span id="__span-9-5"><a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">await</span><span class="w"> </span><span class="nx">resp</span><span class="p">.</span><span class="nx">json</span><span class="p">());</span>
|
|
</span></code></pre></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admonition tip">
|
|
<p class="admonition-title">The reference client sets it for you</p>
|
|
<p>unshackle's own <a href="../../../guide/downloading/">remote download</a> client (<code>RemoteService</code>) sends <code>X-Secret-Key</code> automatically, reading the API key from your <code>remote_services</code> config. It also sends a <code>User-Agent</code> of <code>unshackle/<version></code>. You only deal with the header directly when writing your own client.</p>
|
|
</div>
|
|
<h3 id="the-health-check-needs-an-api-key-too">The health check needs an API key too<a class="headerlink" href="#the-health-check-needs-an-api-key-too" title="Permanent link">¶</a></h3>
|
|
<p><code>GET /api/health</code> reports the running version and commit, so it sits behind the same <code>X-Secret-Key</code> check as every other route. A monitor that probes it sends an API key; a request without one gets the usual <code>401</code> body, which still proves the server is up:</p>
|
|
<div class="language-bash highlight"><span class="filename">Liveness probe</span><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a>curl<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-Secret-Key: your-secret-key-here"</span><span class="w"> </span>http://127.0.0.1:8786/api/health
|
|
</span></code></pre></div>
|
|
<div class="language-json highlight"><span class="filename">Response</span><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="p">{</span>
|
|
</span><span id="__span-11-2"><a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="p">,</span>
|
|
</span><span id="__span-11-3"><a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a><span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.3.0"</span><span class="p">,</span>
|
|
</span><span id="__span-11-4"><a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a><span class="w"> </span><span class="nt">"update_check"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
|
</span><span id="__span-11-5"><a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a><span class="w"> </span><span class="nt">"update_available"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span>
|
|
</span><span id="__span-11-6"><a id="__codelineno-11-6" name="__codelineno-11-6" href="#__codelineno-11-6"></a><span class="w"> </span><span class="nt">"current_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.3.0"</span><span class="p">,</span>
|
|
</span><span id="__span-11-7"><a id="__codelineno-11-7" name="__codelineno-11-7" href="#__codelineno-11-7"></a><span class="w"> </span><span class="nt">"latest_version"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span>
|
|
</span><span id="__span-11-8"><a id="__codelineno-11-8" name="__codelineno-11-8" href="#__codelineno-11-8"></a><span class="w"> </span><span class="p">}</span>
|
|
</span><span id="__span-11-9"><a id="__codelineno-11-9" name="__codelineno-11-9" href="#__codelineno-11-9"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<p>Every other endpoint (including the Swagger UI at <code>/api/docs/</code> on the API-only server) requires a valid API key.</p>
|
|
<h2 id="authentication-failures">Authentication failures<a class="headerlink" href="#authentication-failures" title="Permanent link">¶</a></h2>
|
|
<p>When the server rejects a request for authentication reasons, the response has an HTTP <code>401 Unauthorized</code> status and a small JSON body.</p>
|
|
<div class="tabbed-set tabbed-alternate" data-tabs="2:2"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><input id="__tabbed_2_2" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Missing header</label><label for="__tabbed_2_2">Wrong key</label></div>
|
|
<div class="tabbed-content">
|
|
<div class="tabbed-block">
|
|
<p>Sending a request with no <code>X-Secret-Key</code> header:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="p">{</span>
|
|
</span><span id="__span-12-2"><a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">401</span><span class="p">,</span>
|
|
</span><span id="__span-12-3"><a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Secret Key is Empty."</span>
|
|
</span><span id="__span-12-4"><a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
</div>
|
|
<div class="tabbed-block">
|
|
<p>Sending an API key that is not in your config:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="p">{</span>
|
|
</span><span id="__span-13-2"><a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">401</span><span class="p">,</span>
|
|
</span><span id="__span-13-3"><a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a><span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Secret Key is Invalid."</span>
|
|
</span><span id="__span-13-4"><a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title">Auth errors use a different JSON shape than other errors (developer note)</p>
|
|
<p>The rest of the API reports errors with a structured body containing <code>error_code</code>, <code>message</code>, <code>timestamp</code>, and other fields (see <a href="../errors/">Errors</a>). The authentication middleware is different: it returns <code>{"status": 401, "message": "..."}</code>, where <code>status</code> is an <strong>integer</strong>, and there is no <code>error_code</code> or <code>timestamp</code>.</p>
|
|
<p>In your client, treat any <code>401</code> as an authentication problem and parse <em>both</em> shapes, because the standard error body is not always present.</p>
|
|
</div>
|
|
<h2 id="rate-limit-rejections">Rate limit rejections<a class="headerlink" href="#rate-limit-rejections" title="Permanent link">¶</a></h2>
|
|
<p>An API key that carries a rate limit gets <code>429 Too Many Requests</code> once it goes over, from the
|
|
same middleware and in the same shape as the <code>401</code> bodies above, with a <code>Retry-After</code> header in
|
|
seconds:</p>
|
|
<div class="language-json highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="p">{</span>
|
|
</span><span id="__span-14-2"><a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a><span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">429</span><span class="p">,</span>
|
|
</span><span id="__span-14-3"><a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Rate limit exceeded."</span>
|
|
</span><span id="__span-14-4"><a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="p">}</span>
|
|
</span></code></pre></div>
|
|
<p>A limit comes from <code>serve.tiers.<name>.rate_limit</code> or from the API key's own
|
|
<code>serve.users.<key>.rate_limit</code>, in requests per hour. An API key with neither has no limit, and
|
|
<code>GET /api/health</code> is never limited. See
|
|
<a href="../dashboard/#rate-limits">dashboard rate limits</a> for the config and the counters, and
|
|
<a href="../../../reference/configuration/services/">services configuration</a> for the keys themselves.</p>
|
|
<div class="admonition warning">
|
|
<p class="admonition-title"><code>429</code> is not the <code>RATE_LIMITED</code> error body</p>
|
|
<p><code>RATE_LIMITED</code> in <a href="../errors/">Errors</a> is a <em>service</em> rate limit that a handler raises, and it
|
|
arrives in the standard envelope with an <code>error_code</code>. The <code>429</code> here comes from the
|
|
middleware before any handler runs, and has the integer-<code>status</code> shape shown above. A client
|
|
that reads <code>error_code</code> to classify a <code>429</code> must cope with it being absent.</p>
|
|
</div>
|
|
<h2 id="access-controls-on-top-of-the-api-key">Access controls on top of the API key<a class="headerlink" href="#access-controls-on-top-of-the-api-key" title="Permanent link">¶</a></h2>
|
|
<p>After the server authenticates a request, several additional controls decide what that specific API key can do.</p>
|
|
<h3 id="per-key-service-allowlists">Per-key service allowlists<a class="headerlink" href="#per-key-service-allowlists" title="Permanent link">¶</a></h3>
|
|
<p>You can restrict which services an API key may use. Two layers combine:</p>
|
|
<ul>
|
|
<li><strong>Global allowlist</strong>: <code>serve.services</code>, applied to every API key.</li>
|
|
<li><strong>Per-key allowlist</strong>: <code>services</code> inside that key's <code>users</code> entry.</li>
|
|
</ul>
|
|
<p>The effective allowlist for a request is:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Global set?</th>
|
|
<th>Per-key set?</th>
|
|
<th>Result</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Yes</td>
|
|
<td>Yes</td>
|
|
<td>The <strong>intersection</strong> of the two</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Yes</td>
|
|
<td>No</td>
|
|
<td>The global list</td>
|
|
</tr>
|
|
<tr>
|
|
<td>No</td>
|
|
<td>Yes</td>
|
|
<td>The per-key list</td>
|
|
</tr>
|
|
<tr>
|
|
<td>No</td>
|
|
<td>No</td>
|
|
<td>No restriction, all services allowed</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="language-yaml highlight"><span class="filename">Restricting services</span><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="nt">serve</span><span class="p">:</span>
|
|
</span><span id="__span-15-2"><a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="w"> </span><span class="nt">api_secret</span><span class="p">:</span><span class="w"> </span><span class="s">"admin-key"</span>
|
|
</span><span id="__span-15-3"><a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="w"> </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"EXAMPLE1"</span><span class="p p-Indicator">,</span><span class="w"> </span><span class="s">"EXAMPLE2"</span><span class="p p-Indicator">,</span><span class="w"> </span><span class="s">"EXAMPLE3"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># global ceiling for all keys</span>
|
|
</span><span id="__span-15-4"><a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a><span class="w"> </span><span class="nt">users</span><span class="p">:</span>
|
|
</span><span id="__span-15-5"><a id="__codelineno-15-5" name="__codelineno-15-5" href="#__codelineno-15-5"></a><span class="w"> </span><span class="nt">limited-key</span><span class="p">:</span>
|
|
</span><span id="__span-15-6"><a id="__codelineno-15-6" name="__codelineno-15-6" href="#__codelineno-15-6"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">limited</span>
|
|
</span><span id="__span-15-7"><a id="__codelineno-15-7" name="__codelineno-15-7" href="#__codelineno-15-7"></a><span class="w"> </span><span class="nt">services</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"EXAMPLE1"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># this key ends up allowed only EXAMPLE1</span>
|
|
</span></code></pre></div>
|
|
<p>Endpoints that name a service (listing services, searching, listing titles/tracks, downloading, profiles, history, and remote sessions) all filter against this allowlist. The server rejects a request for a service outside the API key's allowlist as an invalid service. The server matches service tags after normalisation, so casing and aliases give the same result.</p>
|
|
<h3 id="cdm-overrides-and-per-job-credentials">CDM overrides and per-job credentials<a class="headerlink" href="#cdm-overrides-and-per-job-credentials" title="Permanent link">¶</a></h3>
|
|
<p>The server config gates two download-time capabilities. Unless you enable them, the server refuses them with <code>403 Forbidden</code>, even for an authenticated API key:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Config key</th>
|
|
<th>Default</th>
|
|
<th>Controls</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>serve.cdm_overrides</code></td>
|
|
<td>off</td>
|
|
<td>Whether a request may pick a specific CDM device via the <code>cdm</code> field. Set to <code>true</code> to allow any, or to a list/set of device names to allow only those.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>serve.allow_job_credentials</code></td>
|
|
<td>off</td>
|
|
<td>Whether a request may pass its own <code>credential</code> / <code>credentials</code> login details for a download job.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<div class="language-yaml highlight"><span class="filename">Opting in to per-request CDM and credentials</span><pre><span></span><code><span id="__span-16-1"><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="nt">serve</span><span class="p">:</span>
|
|
</span><span id="__span-16-2"><a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="w"> </span><span class="nt">api_secret</span><span class="p">:</span><span class="w"> </span><span class="s">"admin-key"</span>
|
|
</span><span id="__span-16-3"><a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a><span class="w"> </span><span class="nt">cdm_overrides</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"my_widevine_device"</span><span class="p p-Indicator">]</span><span class="w"> </span><span class="c1"># or true to allow any configured device</span>
|
|
</span><span id="__span-16-4"><a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a><span class="w"> </span><span class="nt">allow_job_credentials</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
|
|
</span></code></pre></div>
|
|
<div class="admonition tip">
|
|
<p class="admonition-title">When to enable these gates</p>
|
|
<p>The two gates are fully independent, and you can combine them. For a locked-down or public-facing deployment, leave <strong>both</strong> unset. They then default off and reject with <code>403</code>, so client requests can never steer the server's CDM or feed it their own logins. Enable them only for a trusted single-client deployment, where the caller and the operator are effectively the same party.</p>
|
|
</div>
|
|
<div class="admonition note">
|
|
<p class="admonition-title">Client-supplied credentials get isolated token caches</p>
|
|
<p>When you enable <code>allow_job_credentials</code> and a download job supplies its own <code>credential</code> / <code>credentials</code>, the server gives each distinct credential its <strong>own</strong> isolated token cache. The server never shares or merges client-supplied logins with its configured-credential caches. They cannot cross-contaminate or read the server's own token state, and they do not clobber each other.</p>
|
|
</div>
|
|
<p>See <a href="../endpoints/">Downloads</a> for how the download endpoint uses these fields.</p>
|
|
<h3 id="session-ip-binding-remote-sessions">Session IP binding (remote sessions)<a class="headerlink" href="#session-ip-binding-remote-sessions" title="Permanent link">¶</a></h3>
|
|
<p>The remote-download session endpoints (<code>/api/session/*</code>) add an extra check on top of the API key. When the server creates a remote session, it records the client's IP address. Any later request for that remote session must come from the <strong>same</strong> IP, or the server rejects it with <code>403 Forbidden</code>. Thus the server binds a remote session both to the API key that created it and to the address it came from.</p>
|
|
<h2 id="transport-security-https">Transport security (HTTPS)<a class="headerlink" href="#transport-security-https" title="Permanent link">¶</a></h2>
|
|
<p>The API server speaks plain HTTP. Because the client sends your API key on every request, do not use it over an unencrypted connection on an untrusted network. To serve over HTTPS, operate a reverse proxy in front of unshackle. The <code>serve</code> command can launch Caddy for you:</p>
|
|
<div class="language-bash highlight"><pre><span></span><code><span id="__span-17-1"><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a>unshackle<span class="w"> </span>serve<span class="w"> </span>--caddy
|
|
</span></code></pre></div>
|
|
<p>With <code>--caddy</code>, unshackle also starts <code>caddy run</code> using the <code>Caddyfile</code> located next to your unshackle config, letting Caddy end the TLS connection and reverse-proxy to the API. Configuring Caddy itself (certificates, domains, upstream) is done in that <code>Caddyfile</code>.</p>
|
|
<h2 id="quick-reference">Quick reference<a class="headerlink" href="#quick-reference" title="Permanent link">¶</a></h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Item</th>
|
|
<th>Value</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Auth header</td>
|
|
<td><code>X-Secret-Key</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Query-parameter alternative</td>
|
|
<td><code>?secret_key=</code> on <code>GET /api/download/jobs/{job_id}/events</code> only</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Default bind address</td>
|
|
<td><code>127.0.0.1:8786</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Failure status</td>
|
|
<td><code>401 Unauthorized</code>, or <code>429 Too Many Requests</code> for a key over its rate limit</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Missing-header body</td>
|
|
<td><code>{"status": 401, "message": "Secret Key is Empty."}</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Invalid-key body</td>
|
|
<td><code>{"status": 401, "message": "Secret Key is Invalid."}</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Over-limit body</td>
|
|
<td><code>{"status": 429, "message": "Rate limit exceeded."}</code>, plus a <code>Retry-After</code> header</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Rate limit config</td>
|
|
<td><code>serve.tiers.<name>.rate_limit</code>, overridden by <code>serve.users.<key>.rate_limit</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Disable auth</td>
|
|
<td><code>--no-key</code> (all requests allowed)</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Config location</td>
|
|
<td><code>serve.api_secret</code> and <code>serve.users</code> in <code>unshackle.yaml</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<hr />
|
|
<h2 id="developer-notes-how-the-server-does-the-check">Developer notes: how the server does the check<a class="headerlink" href="#developer-notes-how-the-server-does-the-check" title="Permanent link">¶</a></h2>
|
|
<div class="admonition note">
|
|
<p class="admonition-title">This section is for people writing or debugging the server, not for API consumers.</p>
|
|
</div>
|
|
<h3 id="two-middlewares-one-header">Two middlewares, one header<a class="headerlink" href="#two-middlewares-one-header" title="Permanent link">¶</a></h3>
|
|
<p>The header and config are identical between modes, but the code that enforces them differs:</p>
|
|
<ul>
|
|
<li>
|
|
<p><strong>API-only server</strong> (<code>--api-only</code>, or <code>--remote-only</code> which forces API-only). An <code>api_key_authentication</code> middleware runs on every request. It reads <code>X-Secret-Key</code> on every request, <code>GET /api/health</code> included. An empty header returns the <em>"Secret Key is Empty."</em> body. A header that is not a configured user returns <em>"Secret Key is Invalid."</em>. In this mode the accepted-keys map holds your <code>api_secret</code> (labelled <code>api_user</code>, with no devices) plus every key under <code>serve.users</code>. A per-user key therefore authenticates here exactly as it does on the integrated server, and that user's <code>services</code> allowlist applies as usual. With <code>--no-key</code>, this middleware is not installed and the users map is empty.</p>
|
|
</li>
|
|
<li>
|
|
<p><strong>Integrated server</strong> (default). A <code>serve_authentication</code> middleware wraps pywidevine's authentication (and pyplayready's for <code>/playready*</code> paths), and the REST routes live in the same application, guarded by the same check. Here the accepted-keys map is your full <code>serve</code> config: <code>api_secret</code> (auto-added with access to all loaded devices) plus every entry under <code>users</code>, each with its declared <code>devices</code> / <code>playready_devices</code>.</p>
|
|
</li>
|
|
</ul>
|
|
<p>Because the API-only middleware guards <em>all</em> non-health paths, the Swagger UI at <code>/api/docs/</code> is also behind <code>X-Secret-Key</code> there. In <code>--remote-only</code> mode the Swagger UI is not mounted at all.</p>
|
|
<p>Both middlewares read the caller's API key through the shared <code>request_secret_key()</code> helper in <code>unshackle/core/api/handlers.py</code>. That helper returns the <code>X-Secret-Key</code> header, and falls back to the <code>secret_key</code> query parameter only when the matched route is the job event stream. The integrated server needs one extra branch for this. pywidevine's <code>authentication</code> is header-only. So <code>serve_authentication</code> compares the API key from the query parameter itself when the header is absent, and delegates to pywidevine in every other case. That is why both modes reject a present-but-wrong header.</p>
|
|
<h3 id="cors-and-the-authorization-header">CORS and the <code>Authorization</code> header<a class="headerlink" href="#cors-and-the-authorization-header" title="Permanent link">¶</a></h3>
|
|
<p>A <code>cors_middleware</code> runs on every response in both modes (and answers <code>OPTIONS</code> preflight requests with an empty response). It emits:</p>
|
|
<div class="language-text highlight"><pre><span></span><code><span id="__span-18-1"><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>Access-Control-Allow-Origin: *
|
|
</span><span id="__span-18-2"><a id="__codelineno-18-2" name="__codelineno-18-2" href="#__codelineno-18-2"></a>Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
|
</span><span id="__span-18-3"><a id="__codelineno-18-3" name="__codelineno-18-3" href="#__codelineno-18-3"></a>Access-Control-Allow-Headers: Content-Type, X-Secret-Key, Authorization
|
|
</span><span id="__span-18-4"><a id="__codelineno-18-4" name="__codelineno-18-4" href="#__codelineno-18-4"></a>Access-Control-Max-Age: 3600
|
|
</span></code></pre></div>
|
|
<p>The REST API authenticates with <code>X-Secret-Key</code>. The middleware advertises <code>Authorization</code> because the co-hosted pywidevine / pyplayready CDM endpoints use it. <code>PUT</code> appears in the allowed-methods list, but no REST route uses it. Only <code>GET</code>, <code>POST</code>, and <code>DELETE</code> have routes.</p>
|
|
<h3 id="where-device-access-is-read">Where device access is read<a class="headerlink" href="#where-device-access-is-read" title="Permanent link">¶</a></h3>
|
|
<p>The map of accepted API keys lives at <code>request.app["config"]["users"]</code>. For DRM operations, an API key's permitted Widevine devices come from its <code>devices</code> list, and PlayReady devices from <code>playready_devices</code>. The server uses the first configured device when a request does not (or may not) override it. A server-wide <code>config.cdm[<service>]</code> mapping, if present, takes precedence over the API key's device list when the server chooses which device to use.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</article>
|
|
</div>
|
|
|
|
|
|
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
|
|
|
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
|
</div>
|
|
|
|
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
|
|
Back to top
|
|
</button>
|
|
|
|
</main>
|
|
|
|
<footer class="md-footer">
|
|
|
|
|
|
|
|
<nav class="md-footer__inner md-grid" aria-label="Footer" >
|
|
|
|
|
|
<a href="../quickstart/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Quick Start">
|
|
<div class="md-footer__button md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
|
</div>
|
|
<div class="md-footer__title">
|
|
<span class="md-footer__direction">
|
|
Previous
|
|
</span>
|
|
<div class="md-ellipsis">
|
|
Quick Start
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
|
|
|
|
<a href="../endpoints/" class="md-footer__link md-footer__link--next" aria-label="Next: Endpoints">
|
|
<div class="md-footer__title">
|
|
<span class="md-footer__direction">
|
|
Next
|
|
</span>
|
|
<div class="md-ellipsis">
|
|
Endpoints
|
|
</div>
|
|
</div>
|
|
<div class="md-footer__button md-icon">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
|
|
</div>
|
|
</a>
|
|
|
|
</nav>
|
|
|
|
|
|
<div class="md-footer-meta md-typeset">
|
|
<div class="md-footer-meta__inner md-grid">
|
|
<div class="md-copyright">
|
|
|
|
|
|
Made with
|
|
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
|
Material for MkDocs
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
<div class="md-dialog" data-md-component="dialog">
|
|
<div class="md-dialog__inner md-typeset"></div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script id="__config" type="application/json">{"annotate": null, "base": "../../..", "features": ["content.code.annotate", "content.code.copy", "content.tabs.link", "content.tooltips", "navigation.footer", "navigation.indexes", "navigation.sections", "navigation.tabs", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../../assets/javascripts/workers/search.a7633da7.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
|
|
|
|
|
<script src="../../../assets/javascripts/bundle.c21346df.min.js"></script>
|
|
|
|
|
|
</body>
|
|
</html> |