Spotify.NET/docs/pkce/index.html
2021-06-20 18:52:04 +00:00

39 lines
23 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-alpha.72">
<link rel="alternate" type="application/rss+xml" href="/SpotifyAPI-NET/news/rss.xml" title="SpotifyAPI-NET Blog RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/SpotifyAPI-NET/news/atom.xml" title="SpotifyAPI-NET Blog Atom Feed"><title data-react-helmet="true">PKCE | SpotifyAPI-NET</title><meta data-react-helmet="true" property="og:url" content="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/pkce"><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="PKCE | SpotifyAPI-NET"><meta data-react-helmet="true" name="description" content="The authorization code flow with PKCE is the best option for mobile and desktop applications where it is unsafe to store your client secret. It provides your app with an access token that can be refreshed. For further information about this flow, see IETF RFC-7636."><meta data-react-helmet="true" property="og:description" content="The authorization code flow with PKCE is the best option for mobile and desktop applications where it is unsafe to store your client secret. It provides your app with an access token that can be refreshed. For further information about this flow, see IETF RFC-7636."><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/pkce"><link data-react-helmet="true" rel="alternate" href="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/pkce" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://johnnycrazy.github.io/SpotifyAPI-NET/docs/pkce" hreflang="x-default"><link rel="stylesheet" href="/SpotifyAPI-NET/assets/css/styles.fa980c59.css">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/styles.d00f1e8c.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/runtime~main.dd510751.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/main.fd7c3fe9.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/1.b9625931.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/2.138e3a59.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/56.11f007b6.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/57.e81978c3.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/935f2afb.72b0758a.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/17896441.4c5b76d7.js" as="script">
<link rel="preload" href="/SpotifyAPI-NET/assets/js/e3c15a47.9b271e69.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="#main" 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" type="button" tabindex="0"><svg aria-label="Menu" width="30" height="30" viewBox="0 0 30 30" role="img" focusable="false"><title>Menu</title><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"><strong class="navbar__title">SpotifyAPI-NET</strong></a><div class="navbar__item dropdown dropdown--hoverable dropdown--left"><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">GitHub</a><div class="react-toggle react-toggle--disabled displayOnlyInLargeViewport_GrZ2"><div class="react-toggle-track"><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><div class="react-toggle-thumb"></div><input type="checkbox" disabled="" aria-label="Dark mode toggle" class="react-toggle-screenreader-only"></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div><div class="navbar-sidebar"><div class="navbar-sidebar__brand"><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"><strong class="navbar__title">SpotifyAPI-NET</strong></a></div><div class="navbar-sidebar__items"><div class="menu"><ul class="menu__list"><li class="menu__list-item menu__list-item--collapsed"><a role="button" class="menu__link menu__link--sublist">Docs</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" href="/SpotifyAPI-NET/docs/introduction">6.X (current)</a></li><li class="menu__list-item"><a class="menu__link" href="/SpotifyAPI-NET/docs/5.1.1/home">5.1.1</a></li></ul></li><li class="menu__list-item"><a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET" target="_blank" rel="noopener noreferrer" class="menu__link">GitHub</a></li></ul></div></div></div></nav><div class="main-wrapper main-docs-wrapper"><div class="docPage_31aa"><div class="docSidebarContainer_3Kbt" role="complementary"><div class="sidebar_15mo"><div class="menu menu--responsive thin-scrollbar menu_Bmed"><button aria-label="Open menu" aria-haspopup="true" class="button button--secondary button--sm menu__button" type="button"><svg aria-label="Menu" class="sidebarMenuIcon_fgN0" width="24" height="24" viewBox="0 0 30 30" role="img" focusable="false"><title>Menu</title><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><ul class="menu__list"><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#!">SpotifyAPI-NET</a><ul 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><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/error_handling">Error Handling</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/configuration">Configuration</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/logging">Logging</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/proxy">Proxy</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/pagination">Pagination</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/retry_handling">Retry Handling</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/iplayableitem">IPlayableItem</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/unit_testing">Unit Testing</a></li></ul></li><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#!" tabindex="0">Authentication Guides</a><ul 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 aria-current="page" class="menu__link menu__link--active active" tabindex="0" href="/SpotifyAPI-NET/docs/pkce">PKCE</a></li><li class="menu__list-item"><a class="menu__link" 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><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_asp">ASP.NET</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_blazor_wasm">Blazor WASM</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_blazor">Blazor ServerSide</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_cli_custom_html">CLI - Custom HTML</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_cli_persistent_config">CLI - Persistent Config</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_token_swap">Token Swap</a></li><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/example_uwp">UWP</a></li></ul></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#!" tabindex="0">Migration Guides</a><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="-1" href="/SpotifyAPI-NET/docs/5_to_6">5.x.x to 6.x.x</a></li></ul></li></ul></li></ul></div></div></div><main class="docMainContainer_3ufF"><div class="container padding-vert--lg docItemWrapper_3FMP"><div class="row"><div class="col docItemCol_3FnS"><div class="docItemContainer_33ec"><article><div><span class="badge badge--secondary">Version: 6.X</span></div><header><h1 class="docTitle_3a4h">PKCE</h1></header><div class="markdown"><blockquote><p>The authorization code flow with PKCE is the best option for mobile and desktop applications where it is unsafe to store your client secret. It provides your app with an access token that can be refreshed. For further information about this flow, see <a href="https://tools.ietf.org/html/rfc7636" target="_blank" rel="noopener noreferrer">IETF RFC-7636</a>.</p></blockquote><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="generating-challenge--verifier"></a>Generating Challenge &amp; Verifier<a class="hash-link" href="#generating-challenge--verifier" title="Direct link to heading">#</a></h2><p>For every authentication request, a verify code and its challenge code needs to be generated. The class <code>PKCEUtil</code> can be used to generate those, either with random generated or self supplied values:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><div tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar"><div class="codeBlockLines_39YC" style="color:#bfc7d5;background-color:#292d3e"><div class="token-line" style="color:#bfc7d5"><span class="token plain">// Generates a secure random verifier of length 100 and its challenge</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var (verifier, challenge) = PKCEUtil.GenerateCodes();</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">// Generates a secure random verifier of length 120 and its challenge</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var (verifier, challenge) = PKCEUtil.GenerateCodes(120);</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">// Returns the passed string and its challenge (Make sure it&#x27;s random and long enough)</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var (verifier, challenge) = PKCEUtil.GenerateCodes(&quot;YourSecureRandomString&quot;);</span></div></div></div><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor enhancedAnchor_2LWZ" id="generating-login-uri"></a>Generating Login URI<a class="hash-link" href="#generating-login-uri" title="Direct link to heading">#</a></h2><p>Like most auth flows, you&#x27;ll need to redirect your user to Spotify&#x27;s servers so they are able to grant access to your application:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><div tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar"><div class="codeBlockLines_39YC" style="color:#bfc7d5;background-color:#292d3e"><div class="token-line" style="color:#bfc7d5"><span class="token plain">// Make sure &quot;http://localhost:5000/callback&quot; is in your applications redirect URIs!</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var loginRequest = new LoginRequest(</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> new Uri(&quot;http://localhost:5000/callback&quot;),</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> &quot;YourClientId&quot;,</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> LoginRequest.ResponseType.Code</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">)</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">{</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> CodeChallengeMethod = &quot;S256&quot;,</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> CodeChallenge = challenge,</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative }</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">};</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var uri = loginRequest.ToUri();</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">// Redirect user to uri via your favorite web-server or open a local browser window</span></div></div></div><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o">Copy</button></div></div><p>When the user is redirected to the generated uri, they will have to login with their Spotify account and confirm that your application wants to access their user data. Once confirmed, they will be redirected to <code>http://localhost:5000/callback</code> and a <code>code</code> parameter is attached to the query. The redirect URI can also contain a custom protocol paired with UWP App Custom Protocol handler. This received <code>code</code> has to be exchanged for an <code>access_token</code> and <code>refresh_token</code>:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><div tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar"><div class="codeBlockLines_39YC" style="color:#bfc7d5;background-color:#292d3e"><div class="token-line" style="color:#bfc7d5"><span class="token plain">// This method should be called from your web-server when the user visits &quot;http://localhost:5000/callback&quot;</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">public Task GetCallback(string code)</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">{</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> // Note that we use the verifier calculated above!</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> var initialResponse = await new OAuthClient().RequestToken(</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> new PKCETokenRequest(&quot;ClientId&quot;, code, &quot;http://localhost:5000&quot;, verifier)</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> );</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> var spotify = new SpotifyClient(initialResponse.AccessToken);</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> // Also important for later: response.RefreshToken</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">}</span></div></div></div><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o">Copy</button></div></div><p>With PKCE you can also refresh tokens once they&#x27;re expired:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><div tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar"><div class="codeBlockLines_39YC" style="color:#bfc7d5;background-color:#292d3e"><div class="token-line" style="color:#bfc7d5"><span class="token plain">var newResponse = await new OAuthClient().RequestToken(</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> new PKCETokenRefreshRequest(&quot;ClientId&quot;, initialResponse.RefreshToken)</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">);</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var spotify = new SpotifyClient(newResponse.AccessToken);</span></div></div></div><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o">Copy</button></div></div><p>If you do not want to take care of manually refreshing tokens, you can use <code>PKCEAuthenticator</code>:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly csharp"><div tabindex="0" class="prism-code language-csharp codeBlock_23N8 thin-scrollbar"><div class="codeBlockLines_39YC" style="color:#bfc7d5;background-color:#292d3e"><div class="token-line" style="color:#bfc7d5"><span class="token plain">var authenticator = new PKCEAuthenticator(clientId, initialResponse);</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block">
</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var config = SpotifyClientConfig.CreateDefault()</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain"> .WithAuthenticator(authenticator);</span></div><div class="token-line" style="color:#bfc7d5"><span class="token plain">var spotify = new SpotifyClient(config);</span></div></div></div><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o">Copy</button></div></div></div></article><div class="margin-vert--xl"><div class="row"><div class="col"><a href="https://github.com/JohnnyCrazy/SpotifyAPI-NET/edit/master/SpotifyAPI.Docs/docs/pkce.md" target="_blank" rel="noreferrer noopener"><svg fill="currentColor" height="1.2em" width="1.2em" preserveAspectRatio="xMidYMid meet" role="img" viewBox="0 0 40 40" class="iconEdit_2_ui" aria-label="Edit page"><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 text--right"><em><small>Last updated on <time datetime="2021-06-20T18:50:41.000Z" class="lastUpdatedDate_1WI_">6/20/2021</time> by <strong>dependabot[bot]</strong></small></em></div></div></div><div class="margin-vert--lg"><nav class="pagination-nav" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/SpotifyAPI-NET/docs/authorization_code"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« Authorization Code</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/SpotifyAPI-NET/docs/token_swap"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Token Swap »</div></a></div></nav></div></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="#generating-challenge--verifier" class="table-of-contents__link">Generating Challenge &amp; Verifier</a></li><li><a href="#generating-login-uri" class="table-of-contents__link">Generating Login URI</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 © 2021 Jonas Dellinger. Built with Docusaurus.</div></div></div></footer></div>
<script src="/SpotifyAPI-NET/assets/js/styles.d00f1e8c.js"></script>
<script src="/SpotifyAPI-NET/assets/js/runtime~main.dd510751.js"></script>
<script src="/SpotifyAPI-NET/assets/js/main.fd7c3fe9.js"></script>
<script src="/SpotifyAPI-NET/assets/js/1.b9625931.js"></script>
<script src="/SpotifyAPI-NET/assets/js/2.138e3a59.js"></script>
<script src="/SpotifyAPI-NET/assets/js/56.11f007b6.js"></script>
<script src="/SpotifyAPI-NET/assets/js/57.e81978c3.js"></script>
<script src="/SpotifyAPI-NET/assets/js/935f2afb.72b0758a.js"></script>
<script src="/SpotifyAPI-NET/assets/js/17896441.4c5b76d7.js"></script>
<script src="/SpotifyAPI-NET/assets/js/e3c15a47.9b271e69.js"></script>
</body>
</html>