mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-23 14:46:26 +00:00
19 lines
22 KiB
HTML
19 lines
22 KiB
HTML
<!doctype html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<meta name="generator" content="Docusaurus v2.0.0-beta.4">
|
|
<title data-react-helmet="true">Token Swap | SpotifyAPI-NET</title><meta data-react-helmet="true" property="og:url" content="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/token_swap"><meta data-react-helmet="true" name="docusaurus_locale" content="en"><meta data-react-helmet="true" name="docusaurus_version" content="current"><meta data-react-helmet="true" name="docusaurus_tag" content="docs-default-current"><meta data-react-helmet="true" property="og:title" content="Token Swap | SpotifyAPI-NET"><meta data-react-helmet="true" name="description" content="Token Swap provides an authenticatiow flow where client-side apps (like CLI/desktop/mobile apps) are still able to use long-living tokens and the opportunity to refresh them without exposing your application's secret. This however requires a server-side part to work."><meta data-react-helmet="true" property="og:description" content="Token Swap provides an authenticatiow flow where client-side apps (like CLI/desktop/mobile apps) are still able to use long-living tokens and the opportunity to refresh them without exposing your application's secret. This however requires a server-side part to work."><link data-react-helmet="true" rel="shortcut icon" href="/SpotifyAPI-NET/img/favicon.ico"><link data-react-helmet="true" rel="canonical" href="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/token_swap"><link data-react-helmet="true" rel="alternate" href="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/token_swap" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/token_swap" hreflang="x-default"><link rel="stylesheet" href="/SpotifyAPI-NET/assets/css/styles.834af7f3.css">
|
|
<link rel="preload" href="/SpotifyAPI-NET/assets/js/runtime~main.1007aa21.js" as="script">
|
|
<link rel="preload" href="/SpotifyAPI-NET/assets/js/main.29a3befa.js" as="script">
|
|
</head>
|
|
<body>
|
|
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
|
|
<div><a href="#" class="skipToContent_1oUP">Skip to main content</a></div><nav class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/SpotifyAPI-NET/"><img src="/SpotifyAPI-NET/img/logo.svg" alt="SpotifyAPI-NET" class="themedImage_1VuW themedImage--light_3UqQ navbar__logo"><img src="/SpotifyAPI-NET/img/logo.svg" alt="SpotifyAPI-NET" class="themedImage_1VuW themedImage--dark_hz6m navbar__logo"><b class="navbar__title">SpotifyAPI-NET</b></a><div class="navbar__item dropdown dropdown--hoverable"><a class="navbar__item navbar__link">Docs</a><ul class="dropdown__menu"><li><a class="dropdown__link" href="/SpotifyAPI-NET/docs/introduction">6.X (current)</a></li><li><a class="dropdown__link" href="/SpotifyAPI-NET/docs/5.1.1/home">5.1.1</a></li></ul></div></div><div class="navbar__items navbar__items--right"><a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link"><span>GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_3J9K"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a><div class="react-toggle toggle_3Zt9 react-toggle--disabled"><div class="react-toggle-track" role="button" tabindex="-1"><div class="react-toggle-track-check"><span class="toggle_71bT">🌜</span></div><div class="react-toggle-track-x"><span class="toggle_71bT">🌞</span></div><div class="react-toggle-thumb"></div></div><input type="checkbox" class="react-toggle-screenreader-only" aria-label="Switch between dark and light mode"></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div class="main-wrapper docs-wrapper doc-page"><div class="docPage_31aa"><button class="clean-btn backToTopButton_35hR" type="button" title="Scroll to top"><svg viewBox="0 0 24 24" width="28"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="currentColor"></path></svg></button><aside class="docSidebarContainer_3Kbt"><div class="sidebar_15mo"><nav class="menu thin-scrollbar menu_Bmed menuWithAnnouncementBar_2WvA"><ul class="menu__list"><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#">SpotifyAPI-NET</a><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/introduction">Introduction</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/getting_started">Getting Started</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#" tabindex="0">Guides</a></li><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#" tabindex="0">Authentication Guides</a><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/auth_introduction">Introduction</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/client_credentials">Client Credentials</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/implicit_grant">Implicit Grant</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/authorization_code">Authorization Code</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/pkce">PKCE</a></li><li class="menu__list-item"><a aria-current="page" class="menu__link menu__link--active active" tabindex="0" href="/SpotifyAPI-NET/docs/token_swap">Token Swap</a></li></ul></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/SpotifyAPI-NET/docs/showcase">Showcase</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#" tabindex="0">Examples</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#" tabindex="0">Migration Guides</a></li></ul></li></ul></nav></div></aside><main class="docMainContainer_3ufF"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_3FnS"><div class="docItemContainer_33ec"><article><span class="badge badge--secondary">Version: 6.X</span><div class="tocCollapsible_1PrD tocMobile_3Hoh"><button type="button" class="clean-btn tocCollapsibleButton_2O1e">On this page</button></div><div class="markdown"><header><h1 class="h1Heading_27L5">Token Swap</h1></header><p>Token Swap provides an authenticatiow flow where client-side apps (like CLI/desktop/mobile apps) are still able to use long-living tokens and the opportunity to refresh them without exposing your application's secret. This however requires a server-side part to work.</p><p>It is based on the <a href="/SpotifyAPI-NET/docs/authorization_code">Authorization Code</a> flow and is also documented by Spotify: <a href="https://developer.spotify.com/documentation/ios/guides/token-swap-and-refresh/" target="_blank" rel="noopener noreferrer">Token Swap and Refresh </a>.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="flow"></a>Flow<a class="hash-link" href="#flow" title="Direct link to heading">#</a></h2><p>The client uses the first part of the <code>Authorization Code</code> flow and redirects the user to Spotify's login page. In this part, only the client id is required. Once the user logged in and confirmed the usage of your app, they will be redirect to a <code>http://localhost</code> server which grabs the <code>code</code> from the query parameters.</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><pre tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token plain">var request = new LoginRequest("http://localhost", "ClientId", LoginRequest.ResponseType.Code)</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> Scope = new List<string> { Scopes.UserReadEmail }</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">};</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">BrowserUtil.Open(uri);</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>Now, swapping out this <code>code</code> for an <code>access_token</code> would require the app's client secret. We don't have this on the client-side. Instead, we send a request to our server, which takes care of the code swap:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><pre tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token plain">public Task GetCallback(string code)</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">{</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> var response = await new OAuthClient().RequestToken(</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new TokenSwapTokenRequest("https://your-swap-server.com/swap", code)</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> );</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
|
|
</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> var spotify = new SpotifyClient(response.AccessToken);</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> // Also important for later: response.RefreshToken</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>The server swapped out the <code>code</code> for an <code>access_token</code> and <code>refresh_token</code>. Once we realize the <code>access_token</code> expired, we can also ask the server to refresh it:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><pre tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token plain">// if response.IsExpired is true</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">var newResponse = await new OAuthClient().RequestToken(</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> new TokenSwapTokenRequest("https://your-swap-server.com/refresh", response.RefreshToken)</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">);</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
|
|
</span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">var spotify = new SpotifyClient(newResponse.AccessToken);</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="server-implementation"></a>Server Implementation<a class="hash-link" href="#server-implementation" title="Direct link to heading">#</a></h2><p>The server needs to support two endpoints, <code>/swap</code> and <code>/refresh</code> (endpoints can be named differently of course).</p><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="swap"></a>Swap<a class="hash-link" href="#swap" title="Direct link to heading">#</a></h3><p>The client sends a body via <code>application/x-www-form-urlencoded</code> where the received <code>code</code> is included. In cURL:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly bash"><pre tabindex="0" class="prism-code language-bash codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">curl</span><span class="token plain"> -X POST </span><span class="token string" style="color:rgb(195, 232, 141)">"https://example.com/v1/swap"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> -H </span><span class="token string" style="color:rgb(195, 232, 141)">"Content-Type: application/x-www-form-urlencoded"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> --data </span><span class="token string" style="color:rgb(195, 232, 141)">"code=AQDy8...xMhKNA"</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>The server needs to respond with content-type <code>application/json</code> and the following body:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly json"><pre tabindex="0" class="prism-code language-json codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> </span><span class="token property">"access_token"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"NgAagA...Um_SHo"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> </span><span class="token property">"expires_in"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"3600"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> </span><span class="token property">"refresh_token"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"NgCXRK...MzYjw"</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="refresh"></a>Refresh<a class="hash-link" href="#refresh" title="Direct link to heading">#</a></h3><p>The client sends a body via <code>application/x-www-form-urlencoded</code> where the received <code>refresh_token</code> is included. In cURL:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly bash"><pre tabindex="0" class="prism-code language-bash codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">curl</span><span class="token plain"> -X POST </span><span class="token string" style="color:rgb(195, 232, 141)">"https://example.com/v1/refresh"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> -H </span><span class="token string" style="color:rgb(195, 232, 141)">"Content-Type: application/x-www-form-urlencoded"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> --data </span><span class="token string" style="color:rgb(195, 232, 141)">"refresh_token=NgCXRK...MzYjw"</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>The server needs to respond with content-type <code>application/json</code> and the following body:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly json"><pre tabindex="0" class="prism-code language-json codeBlock_23N8 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#bfc7d5"><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> </span><span class="token property">"access_token"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"NgAagA...Um_SHo"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"> </span><span class="token property">"expires_in"</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"3600"</span><span class="token plain"></span></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="example"></a>Example<a class="hash-link" href="#example" title="Direct link to heading">#</a></h2><p>An example server has been implemented in Node.JS with a .NET CLI client, located at <a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET/tree/master/SpotifyAPI.Web.Examples/Example.TokenSwap" target="_blank" rel="noopener noreferrer">Example.TokenSwap</a>.</p></div><footer class="row docusaurus-mt-lg"><div class="col"><a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET/edit/master/SpotifyAPI.Docs/docs/token_swap.md" target="_blank" rel="noreferrer noopener"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_2_ui" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div><div class="col lastUpdated_3DPF">Last updated on <b><time datetime="2022-05-19T04:06:24.000Z">5/19/2022</time></b> by <b>Alex Yeo</b></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/SpotifyAPI-NET/docs/pkce"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« PKCE</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/SpotifyAPI-NET/docs/showcase"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Showcase »</div></a></div></nav></div></div><div class="col col--3"><div class="tableOfContents_35-E thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#flow" class="table-of-contents__link">Flow</a></li><li><a href="#server-implementation" class="table-of-contents__link">Server Implementation</a><ul><li><a href="#swap" class="table-of-contents__link">Swap</a></li><li><a href="#refresh" class="table-of-contents__link">Refresh</a></li></ul></li><li><a href="#example" class="table-of-contents__link">Example</a></li></ul></div></div></div></div></main></div></div><footer class="footer footer--dark"><div class="container"><div class="footer__bottom text--center"><div class="footer__copyright">Copyright © 2022 Jonas Dellinger. Built with Docusaurus.</div></div></div></footer></div>
|
|
<script src="/SpotifyAPI-NET/assets/js/runtime~main.1007aa21.js"></script>
|
|
<script src="/SpotifyAPI-NET/assets/js/main.29a3befa.js"></script>
|
|
</body>
|
|
</html> |