Spotify.NET/auth/token_swap.html
2019-08-16 22:41:36 +00:00

74 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>TokenSwapAuth | SpotifyAPI-NET</title>
<meta name="description" content=":sound: An API for the Spotify-Client and the Spotify Web API, written in C#/.NET">
<link rel="preload" href="/assets/css/0.styles.a0ada997.css" as="style"><link rel="preload" href="/assets/js/app.cc44386d.js" as="script"><link rel="preload" href="/assets/js/2.db6f1a12.js" as="script"><link rel="preload" href="/assets/js/10.29899bd4.js" as="script"><link rel="prefetch" href="/assets/js/11.0f6f8c29.js"><link rel="prefetch" href="/assets/js/12.2978ac7c.js"><link rel="prefetch" href="/assets/js/13.bf7ffcb9.js"><link rel="prefetch" href="/assets/js/14.76e71857.js"><link rel="prefetch" href="/assets/js/15.a0fc535e.js"><link rel="prefetch" href="/assets/js/16.427ccfec.js"><link rel="prefetch" href="/assets/js/17.a281444b.js"><link rel="prefetch" href="/assets/js/18.1f840ba3.js"><link rel="prefetch" href="/assets/js/19.c6343cc0.js"><link rel="prefetch" href="/assets/js/20.f7f0836a.js"><link rel="prefetch" href="/assets/js/21.700cbb77.js"><link rel="prefetch" href="/assets/js/22.312dbfd1.js"><link rel="prefetch" href="/assets/js/23.d5043e4d.js"><link rel="prefetch" href="/assets/js/24.07659c28.js"><link rel="prefetch" href="/assets/js/25.81029a65.js"><link rel="prefetch" href="/assets/js/26.3876e2f9.js"><link rel="prefetch" href="/assets/js/3.e41cd288.js"><link rel="prefetch" href="/assets/js/4.d34ebd53.js"><link rel="prefetch" href="/assets/js/5.dfb9460f.js"><link rel="prefetch" href="/assets/js/6.06a21323.js"><link rel="prefetch" href="/assets/js/7.6ce7959d.js"><link rel="prefetch" href="/assets/js/8.26b7beaf.js"><link rel="prefetch" href="/assets/js/9.2255e63f.js">
<link rel="stylesheet" href="/assets/css/0.styles.a0ada997.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">SpotifyAPI-NET</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">Home</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">SpotifyAPI.Web</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/web/getting_started.html" class="nav-link">Getting Started</a></li><li class="dropdown-item"><!----> <a href="/web/examples.html" class="nav-link">Examples</a></li><li class="dropdown-item"><!----> <a href="/web/proxy.html" class="nav-link">Proxy</a></li><li class="dropdown-item"><!----> <a href="/web/albums.html" class="nav-link">- Albums</a></li><li class="dropdown-item"><!----> <a href="/web/artists.html" class="nav-link">- Artists</a></li><li class="dropdown-item"><!----> <a href="/web/browse.html" class="nav-link">- Browse</a></li><li class="dropdown-item"><!----> <a href="/web/follow.html" class="nav-link">- Follow</a></li><li class="dropdown-item"><!----> <a href="/web/library.html" class="nav-link">- Library</a></li><li class="dropdown-item"><!----> <a href="/web/personalization.html" class="nav-link">- Personalization</a></li><li class="dropdown-item"><!----> <a href="/web/player.html" class="nav-link">- Player</a></li><li class="dropdown-item"><!----> <a href="/web/playlists.html" class="nav-link">- Playlists</a></li><li class="dropdown-item"><!----> <a href="/web/profiles.html" class="nav-link">- Profiles</a></li><li class="dropdown-item"><!----> <a href="/web/search.html" class="nav-link">- Search</a></li><li class="dropdown-item"><!----> <a href="/web/tracks.html" class="nav-link">- Tracks</a></li><li class="dropdown-item"><!----> <a href="/web/utils.html" class="nav-link">Utilities</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">SpotifyAPI.Auth</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/auth/getting_started.html" class="nav-link">Getting Started</a></li><li class="dropdown-item"><!----> <a href="/auth/implicit_grant.html" class="nav-link">- ImplicitGrantAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/token_swap.html" class="nav-link router-link-exact-active router-link-active">- TokenSwapAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/authorization_code.html" class="nav-link">- AutorizationCodeAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/client_credentials.html" class="nav-link">- ClientCredentialsAuth</a></li></ul></div></div> <a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET" target="_blank" rel="noopener noreferrer" class="repo-link">
🚀 GitHub
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">Home</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">SpotifyAPI.Web</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/web/getting_started.html" class="nav-link">Getting Started</a></li><li class="dropdown-item"><!----> <a href="/web/examples.html" class="nav-link">Examples</a></li><li class="dropdown-item"><!----> <a href="/web/proxy.html" class="nav-link">Proxy</a></li><li class="dropdown-item"><!----> <a href="/web/albums.html" class="nav-link">- Albums</a></li><li class="dropdown-item"><!----> <a href="/web/artists.html" class="nav-link">- Artists</a></li><li class="dropdown-item"><!----> <a href="/web/browse.html" class="nav-link">- Browse</a></li><li class="dropdown-item"><!----> <a href="/web/follow.html" class="nav-link">- Follow</a></li><li class="dropdown-item"><!----> <a href="/web/library.html" class="nav-link">- Library</a></li><li class="dropdown-item"><!----> <a href="/web/personalization.html" class="nav-link">- Personalization</a></li><li class="dropdown-item"><!----> <a href="/web/player.html" class="nav-link">- Player</a></li><li class="dropdown-item"><!----> <a href="/web/playlists.html" class="nav-link">- Playlists</a></li><li class="dropdown-item"><!----> <a href="/web/profiles.html" class="nav-link">- Profiles</a></li><li class="dropdown-item"><!----> <a href="/web/search.html" class="nav-link">- Search</a></li><li class="dropdown-item"><!----> <a href="/web/tracks.html" class="nav-link">- Tracks</a></li><li class="dropdown-item"><!----> <a href="/web/utils.html" class="nav-link">Utilities</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title">SpotifyAPI.Auth</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/auth/getting_started.html" class="nav-link">Getting Started</a></li><li class="dropdown-item"><!----> <a href="/auth/implicit_grant.html" class="nav-link">- ImplicitGrantAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/token_swap.html" class="nav-link router-link-exact-active router-link-active">- TokenSwapAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/authorization_code.html" class="nav-link">- AutorizationCodeAuth</a></li><li class="dropdown-item"><!----> <a href="/auth/client_credentials.html" class="nav-link">- ClientCredentialsAuth</a></li></ul></div></div> <a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET" target="_blank" rel="noopener noreferrer" class="repo-link">
🚀 GitHub
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav> <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>TokenSwapAuth</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/auth/token_swap.html#using-tokenswapwebapifactory" class="sidebar-link">Using TokenSwapWebAPIFactory</a></li><li><a href="/auth/token_swap.html#using-tokenswapauth" class="sidebar-link">Using TokenSwapAuth</a></li><li><a href="/auth/token_swap.html#token-swap-endpoint" class="sidebar-link">Token Swap Endpoint</a></li><li><a href="/auth/token_swap.html#remarks" class="sidebar-link">Remarks</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="tokenswapauth"><a href="#tokenswapauth" aria-hidden="true" class="header-anchor">#</a> TokenSwapAuth</h1> <p>This way uses server-side code or at least access to an exchange server, otherwise, compared to other
methods, it is impossible to use.</p> <p>With this approach, you provide the URI/URL to your desired exchange server to perform all necessary
requests to Spotify, as well as requests that return back to the &quot;server URI&quot;.</p> <p>The exchange server <strong>must</strong> be able to:</p> <ul><li>Return the authorization code from Spotify API authenticate page via GET request to the &quot;server URI&quot;.</li> <li>Request the token response object via POST to the Spotify API token page.</li> <li>Request a refreshed token response object via POST to the Spotify API token page.</li></ul> <p><strong>The good news is that you do not need to code it yourself.</strong></p> <p>The advantages of this method are that the client ID and redirect URI are very well hidden and almost unexposed, but more importantly, your client secret is <strong>never</strong> exposed and is completely hidden compared to other methods (excluding <a href="/SpotifyWebAPI/auth#implicitgrantauth">ImplicitGrantAuth</a>
as it does not deal with a client secret). This means
your Spotify app <strong>cannot</strong> be spoofed by a malicious third party.</p> <h2 id="using-tokenswapwebapifactory"><a href="#using-tokenswapwebapifactory" aria-hidden="true" class="header-anchor">#</a> Using TokenSwapWebAPIFactory</h2> <p>The TokenSwapWebAPIFactory will create and configure a SpotifyWebAPI object for you.</p> <p>It does this through the method GetWebApiAsync <strong>asynchronously</strong>, which means it will not halt execution of your program while obtaining it for you. If you would like to halt execution, which is <strong>synchronous</strong>, use <code>GetWebApiAsync().Result</code> without using <strong>await</strong>.</p> <div class="language-csharp extra-class"><pre class="language-csharp"><code><span class="token class-name">TokenSwapWebAPIFactory</span> webApiFactory<span class="token punctuation">;</span>
<span class="token class-name">SpotifyWebAPI</span> spotify<span class="token punctuation">;</span>
<span class="token comment">// You should store a reference to WebAPIFactory if you are using AutoRefresh or want to manually refresh it later on. New WebAPIFactory objects cannot refresh SpotifyWebAPI object that they did not give to you.</span>
webApiFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TokenSwapWebAPIFactory</span><span class="token punctuation">(</span><span class="token string">&quot;INSERT LINK TO YOUR index.php HERE&quot;</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
Scope <span class="token operator">=</span> Scope<span class="token punctuation">.</span>UserReadPrivate <span class="token operator">|</span> Scope<span class="token punctuation">.</span>UserReadEmail <span class="token operator">|</span> Scope<span class="token punctuation">.</span>PlaylistReadPrivate<span class="token punctuation">,</span>
AutoRefresh <span class="token operator">=</span> <span class="token keyword">true</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// You may want to react to being able to use the Spotify service.</span>
<span class="token comment">// webApiFactory.OnAuthSuccess += (sender, e) =&gt; authorized = true;</span>
<span class="token comment">// You may want to react to your user's access expiring.</span>
<span class="token comment">// webApiFactory.OnAccessTokenExpired += (sender, e) =&gt; authorized = false;</span>
<span class="token keyword">try</span>
<span class="token punctuation">{</span>
spotify <span class="token operator">=</span> <span class="token keyword">await</span> webApiFactory<span class="token punctuation">.</span><span class="token function">GetWebApiAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Synchronous way:</span>
<span class="token comment">// spotify = webApiFactory.GetWebApiAsync().Result;</span>
<span class="token punctuation">}</span>
<span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment">// Example way to handle error reporting gracefully with your SpotifyWebAPI wrapper</span>
<span class="token comment">// UpdateStatus($&quot;Spotify failed to load: {ex.Message}&quot;);</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="using-tokenswapauth"><a href="#using-tokenswapauth" aria-hidden="true" class="header-anchor">#</a> Using TokenSwapAuth</h2> <p>Since the TokenSwapWebAPIFactory not only simplifies the whole process but offers additional functionality too
(such as AutoRefresh and AuthSuccess AuthFailure events), use of this way is very verbose and is only
recommended if you are having issues with TokenSwapWebAPIFactory or need access to the tokens.</p> <div class="language-csharp extra-class"><pre class="language-csharp"><code><span class="token class-name">TokenSwapAuth</span> auth <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">TokenSwapAuth</span><span class="token punctuation">(</span>
exchangeServerUri<span class="token punctuation">:</span> <span class="token string">&quot;INSERT LINK TO YOUR index.php HERE&quot;</span><span class="token punctuation">,</span>
serverUri<span class="token punctuation">:</span> <span class="token string">&quot;http://localhost:4002&quot;</span><span class="token punctuation">,</span>
scope<span class="token punctuation">:</span> Scope<span class="token punctuation">.</span>UserReadPrivate <span class="token operator">|</span> Scope<span class="token punctuation">.</span>UserReadEmail <span class="token operator">|</span> Scope<span class="token punctuation">.</span>PlaylistReadPrivate
<span class="token punctuation">)</span><span class="token punctuation">;</span>
auth<span class="token punctuation">.</span>AuthReceived <span class="token operator">+=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>sender<span class="token punctuation">,</span> response<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
<span class="token punctuation">{</span>
lastToken <span class="token operator">=</span> <span class="token keyword">await</span> auth<span class="token punctuation">.</span><span class="token function">ExchangeCodeAsync</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>Code<span class="token punctuation">)</span><span class="token punctuation">;</span>
spotify <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SpotifyWebAPI</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
TokenType <span class="token operator">=</span> lastToken<span class="token punctuation">.</span>TokenType<span class="token punctuation">,</span>
AccessToken <span class="token operator">=</span> lastToken<span class="token punctuation">.</span>AccessToken
<span class="token punctuation">}</span><span class="token punctuation">;</span>
authenticated <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
auth<span class="token punctuation">.</span><span class="token function">Stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
auth<span class="token punctuation">.</span>OnAccessTokenExpired <span class="token operator">+=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>sender<span class="token punctuation">,</span> e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> spotify<span class="token punctuation">.</span>AccessToken <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">await</span> auth<span class="token punctuation">.</span><span class="token function">RefreshAuthAsync</span><span class="token punctuation">(</span>lastToken<span class="token punctuation">.</span>RefreshToken<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>AccessToken<span class="token punctuation">;</span>
auth<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
auth<span class="token punctuation">.</span><span class="token function">OpenBrowser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="token-swap-endpoint"><a href="#token-swap-endpoint" aria-hidden="true" class="header-anchor">#</a> Token Swap Endpoint</h2> <p>To keep your client secret completely secure and your client ID and redirect URI as secure as possible, use of a web server (such as a php website) is required.</p> <p>To use this method, an external HTTP Server (that you may need to create) needs to be able to supply the following HTTP Endpoints to your application:</p> <p><code>/swap</code> - Swaps out an <code>authorization_code</code> with an <code>access_token</code> and <code>refresh_token</code> - The following parameters are required in the JSON POST Body:</p> <ul><li><code>grant_type</code> (set to <code>&quot;authorization_code&quot;</code>)</li> <li><code>code</code> (the <code>authorization_code</code>)</li> <li><code>redirect_uri</code></li> <li><ul><li><strong>Important</strong> The page that the redirect URI links to must return the authorization code json to your <code>serverUri</code> (default is 'http://localhost:4002') but to the folder 'auth', like this: 'http://localhost:4002/auth'.</li></ul></li></ul> <p><code>/refresh</code> - Refreshes an <code>access_token</code> - The following parameters are required in the JSON POST Body:</p> <ul><li><code>grant_type</code> (set to <code>&quot;refresh_token&quot;</code>)</li> <li><code>refresh_token</code></li></ul> <p>The following open-source token swap endpoint code can be used for your website:</p> <ul><li><a href="https://github.com/rollersteaam/spotify-token-swap-php" target="_blank" rel="noopener noreferrer">rollersteaam/spotify-token-swap-php<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li> <li><a href="https://github.com/simontaen/SpotifyTokenSwap" target="_blank" rel="noopener noreferrer">simontaen/SpotifyTokenSwap<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></li></ul> <h2 id="remarks"><a href="#remarks" aria-hidden="true" class="header-anchor">#</a> Remarks</h2> <p>It should be noted that GitHub Pages does not support hosting php scripts. Hosting php scripts through it will cause the php to render as plain HTML, potentially compromising your client secret while doing absolutely nothing.</p> <p>Be sure you have whitelisted your redirect uri in the Spotify Developer Dashboard otherwise the authorization will always fail.</p> <p>If you did not use the WebAPIFactory or you provided a <code>serverUri</code> different from its default, you must make sure your redirect uri's script at your endpoint will properly redirect to your <code>serverUri</code> (such as changing the areas which refer to <code>localhost:4002</code> if you had changed <code>serverUri</code> from its default), otherwise it will never reach your new <code>serverUri</code>.</p></div> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET/edit/master/SpotifyAPI.Docs/docs/auth/token_swap.md" target="_blank" rel="noopener noreferrer">Help us improve this page!</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div> <div class="last-updated"><span class="prefix">Last Updated: </span> <span class="time">8/16/2019, 10:40:16 PM</span></div></footer> <!----> </main></div><div class="global-ui"></div></div>
<script src="/assets/js/app.cc44386d.js" defer></script><script src="/assets/js/2.db6f1a12.js" defer></script><script src="/assets/js/10.29899bd4.js" defer></script>
</body>
</html>