"use strict";(self.webpackChunkspotify_api_docs=self.webpackChunkspotify_api_docs||[]).push([[1755],{3905:function(e,t,n){n.d(t,{Zo:function(){return c},kt:function(){return h}});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=r.createContext({}),l=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=l(e.components);return r.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,s=e.parentName,c=p(e,["components","mdxType","originalType","parentName"]),u=l(n),h=o,m=u["".concat(s,".").concat(h)]||u[h]||d[h]||a;return n?r.createElement(m,i(i({ref:t},c),{},{components:n})):r.createElement(m,i({ref:t},c))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=u;var p={};for(var s in t)hasOwnProperty.call(t,s)&&(p[s]=t[s]);p.originalType=e,p.mdxType="string"==typeof e?e:o,i[1]=p;for(var l=2;l { Scopes.UserReadEmail }\n};\nBrowserUtil.Open(uri);\n')),(0,a.kt)("p",null,"Now, swapping out this ",(0,a.kt)("inlineCode",{parentName:"p"},"code")," for an ",(0,a.kt)("inlineCode",{parentName:"p"},"access_token")," 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:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-csharp"},'public Task GetCallback(string code)\n{\n var response = await new OAuthClient().RequestToken(\n new TokenSwapTokenRequest("https://your-swap-server.com/swap", code)\n );\n\n var spotify = new SpotifyClient(response.AccessToken);\n // Also important for later: response.RefreshToken\n}\n')),(0,a.kt)("p",null,"The server swapped out the ",(0,a.kt)("inlineCode",{parentName:"p"},"code")," for an ",(0,a.kt)("inlineCode",{parentName:"p"},"access_token")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"refresh_token"),". Once we realize the ",(0,a.kt)("inlineCode",{parentName:"p"},"access_token")," expired, we can also ask the server to refresh it:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-csharp"},'// if response.IsExpired is true\nvar newResponse = await new OAuthClient().RequestToken(\n new TokenSwapTokenRequest("https://your-swap-server.com/refresh", response.RefreshToken)\n);\n\nvar spotify = new SpotifyClient(newResponse.AccessToken);\n')),(0,a.kt)("h2",{id:"server-implementation"},"Server Implementation"),(0,a.kt)("p",null,"The server needs to support two endpoints, ",(0,a.kt)("inlineCode",{parentName:"p"},"/swap")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"/refresh")," (endpoints can be named differently of course)."),(0,a.kt)("h3",{id:"swap"},"Swap"),(0,a.kt)("p",null,"The client sends a body via ",(0,a.kt)("inlineCode",{parentName:"p"},"application/x-www-form-urlencoded")," where the received ",(0,a.kt)("inlineCode",{parentName:"p"},"code")," is included. In cURL:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'curl -X POST "https://example.com/v1/swap"\\\n -H "Content-Type: application/x-www-form-urlencoded"\\\n --data "code=AQDy8...xMhKNA"\n')),(0,a.kt)("p",null,"The server needs to respond with content-type ",(0,a.kt)("inlineCode",{parentName:"p"},"application/json")," and the following body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json"},'{\n "access_token" : "NgAagA...Um_SHo",\n "expires_in" : "3600",\n "refresh_token" : "NgCXRK...MzYjw"\n}\n')),(0,a.kt)("h3",{id:"refresh"},"Refresh"),(0,a.kt)("p",null,"The client sends a body via ",(0,a.kt)("inlineCode",{parentName:"p"},"application/x-www-form-urlencoded")," where the received ",(0,a.kt)("inlineCode",{parentName:"p"},"refresh_token")," is included. In cURL:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},'curl -X POST "https://example.com/v1/refresh"\\\n -H "Content-Type: application/x-www-form-urlencoded"\\\n --data "refresh_token=NgCXRK...MzYjw"\n')),(0,a.kt)("p",null,"The server needs to respond with content-type ",(0,a.kt)("inlineCode",{parentName:"p"},"application/json")," and the following body:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-json"},'{\n "access_token" : "NgAagA...Um_SHo",\n "expires_in" : "3600"\n}\n')),(0,a.kt)("h2",{id:"example"},"Example"),(0,a.kt)("p",null,"An example server has been implemented in Node.JS with a .NET CLI client, located at ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/JohnnyCrazy/SpotifyAPI-NET/tree/master/SpotifyAPI.Web.Examples/Example.TokenSwap"},"Example.TokenSwap"),"."))}d.isMDXComponent=!0}}]);