Skip to content

Commit b293abe

Browse files
committed
Merge branch 'pkce' into master
2 parents 33b7e48 + 38bf925 commit b293abe

File tree

20 files changed

+983
-93
lines changed

20 files changed

+983
-93
lines changed

SpotifyAPI.Docs/.prettierrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"semi": true,
3+
"trailingComma": "all",
4+
"singleQuote": true,
5+
"printWidth": 120,
6+
"tabWidth": 2,
7+
"endOfLine": "auto"
8+
}

SpotifyAPI.Docs/docs/auth_introduction.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
77

88
Spotify does not allow unauthorized access to the api. Thus, you need an access token to make requets. This access token can be gathered via multiple schemes, all following the OAuth2 spec. Since it's important to choose the correct scheme for your usecase, make sure you have a grasp of the following terminology/docs:
99

10-
* OAuth2
11-
* [Spotify Authorization Flows](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow)
10+
- OAuth2
11+
- [Spotify Authorization Flows](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow)
1212

1313
Since every auth flow also needs an application in the [spotify dashboard](https://developer.spotify.com/dashboard/), make sure you have the necessary values (like `Client Id` and `Client Secret`).
1414

1515
Then, continue with the docs of the specific auth flows:
1616

17-
* [Client Credentials](client_credentials.md)
18-
* [Implicit Grant](implicit_grant.md)
19-
* [Authorization Code](authorization_code.md)
20-
* [Token Swap](token_swap.md)
17+
- [Client Credentials](client_credentials.md)
18+
- [Implicit Grant](implicit_grant.md)
19+
- [Authorization Code](authorization_code.md)
20+
- [PKCE](pkce.md)
21+
- [(Token Swap)](token_swap.md)
2122

2223
<img alt="auth comparison" src={useBaseUrl('img/auth_comparison.png')} />

SpotifyAPI.Docs/docs/pkce.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
id: pkce
3+
title: PKCE
4+
---
5+
6+
> 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.
7+
8+
## Generating Challenge & Verifier
9+
10+
For every authentation request, a verify code and its challenge code needs to be generated. The class `PKCEUtil` can be used to generate those, either with random generated or self supplied values:
11+
12+
```csharp
13+
// Generates a secure random verifier of length 100 and its challenge
14+
var (verifier, challenge) = PKCEUtil.GenerateCodes();
15+
16+
// Generates a secure random verifier of length 120 and its challenge
17+
var (verifier, challenge) = PKCEUtil.GenerateCodes(120);
18+
19+
// Returns the passed string and its challenge (Make sure it's random and long enough)
20+
var (verifier, challenge) = PKCEUtil.GenerateCodes("YourSecureRandomString");
21+
```
22+
23+
## Generating Login URI
24+
25+
Like most auth flows, you'll need to redirect your user to spotify's servers so he is able to grant access to your application:
26+
27+
```csharp
28+
// Make sure "http://localhost:5000/callback" is in your applications redirect URIs!
29+
var loginRequest = new LoginRequest(
30+
new Uri("http://localhost:5000/callback"),
31+
"YourClientId",
32+
LoginRequest.ResponseType.Code
33+
)
34+
{
35+
CodeChallengeMethod = "S256",
36+
CodeChallenge = challenge,
37+
Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative }
38+
};
39+
var uri = loginRequest.ToUri();
40+
// Redirect user to uri via your favorite web-server or open a local browser window
41+
```
42+
43+
When the user is redirected to the generated uri, he will have to login with his spotify account and confirm, that your application wants to access his user data. Once confirmed, he will be redirect to `http://localhost:5000/callback` and a `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` has to be exchanged for an `access_token` and `refresh_token`:
44+
45+
```csharp
46+
// This method should be called from your web-server when the user visits "http://localhost:5000/callback"
47+
public Task GetCallback(string code)
48+
{
49+
// Note that we use the verifier calculated above!
50+
var initialResponse = await new OAuthClient().RequestToken(
51+
new PKCETokenRequest("ClientId", code, "http://localhost:5000", verifier)
52+
);
53+
54+
var spotify = new SpotifyClient(initialResponse.AccessToken);
55+
// Also important for later: response.RefreshToken
56+
}
57+
```
58+
59+
With PKCE you can also refresh tokens once they're expired:
60+
61+
```csharp
62+
var newResponse = await new OAuthClient().RequestToken(
63+
new PKCETokenRefreshRequest("ClientId", initialResponse.RefreshToken)
64+
);
65+
66+
var spotify = new SpotifyClient(newResponse.AccessToken);
67+
```
68+
69+
If you do not want to take care of manually refreshing tokens, you can use `PKCEAuthenticator`:
70+
71+
```csharp
72+
var authenticator = new PKCEAuthenticator(clientId, initialResponse);
73+
74+
var config = SpotifyClientConfig.CreateDefault()
75+
.WithAuthenticator(authenticator);
76+
var spotify = new SpotifyClient(config);
77+
```

SpotifyAPI.Docs/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
"swizzle": "docusaurus swizzle",
99
"deploy": "docusaurus deploy"
1010
},
11+
"importSort": {
12+
".js, .jsx, .ts, .tsx": {
13+
"style": "module"
14+
}
15+
},
1116
"dependencies": {
1217
"@docusaurus/core": "^2.0.0-alpha.56",
1318
"@docusaurus/preset-classic": "^2.0.0-alpha.56",
@@ -27,5 +32,10 @@
2732
"last 1 firefox version",
2833
"last 1 safari version"
2934
]
35+
},
36+
"devDependencies": {
37+
"import-sort-style-module": "^6.0.0",
38+
"prettier": "^2.0.5",
39+
"prettier-plugin-import-sort": "^0.0.4"
3040
}
3141
}

SpotifyAPI.Docs/sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module.exports = {
2525
'client_credentials',
2626
'implicit_grant',
2727
'authorization_code',
28+
'pkce',
2829
'token_swap'
2930
]
3031
},
Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,55 @@
1-
import React from "react";
2-
import CodeBlock from '@theme/CodeBlock'
3-
import Tabs from '@theme/Tabs'
4-
import TabItem from '@theme/TabItem'
1+
import CodeBlock from '@theme/CodeBlock';
2+
import TabItem from '@theme/TabItem';
3+
import Tabs from '@theme/Tabs';
4+
import React from 'react';
55

66
// Will be removed after beta releases
77
const VERSION = '6.0.0-beta.9';
88

9-
const installCodeNuget =
10-
`Install-Package SpotifyAPI.Web -Version ${VERSION}
9+
const installCodeNuget = `Install-Package SpotifyAPI.Web -Version ${VERSION}
1110
# Optional Auth module, which includes an embedded HTTP Server for OAuth2
1211
Install-Package SpotifyAPI.Web.Auth -Version ${VERSION}
1312
`;
1413

15-
const installReference =
16-
`<PackageReference Include="SpotifyAPI.Web" Version="${VERSION}" />
14+
const installReference = `<PackageReference Include="SpotifyAPI.Web" Version="${VERSION}" />
1715
<!-- Optional Auth module, which includes an embedded HTTP Server for OAuth2 -->
1816
<PackageReference Include="SpotifyAPI.Web.Auth" Version="${VERSION}" />
1917
`;
2018

21-
const installCodeCLI =
22-
`dotnet add package SpotifyAPI.Web --version ${VERSION}
19+
const installCodeCLI = `dotnet add package SpotifyAPI.Web --version ${VERSION}
2320
# Optional Auth module, which includes an embedded HTTP Server for OAuth2
2421
dotnet add package SpotifyAPI.Web.Auth --version ${VERSION}
2522
`;
2623

2724
const InstallInstructions = () => {
28-
return (<div style={{ padding: '30px' }}>
29-
<Tabs
30-
defaultValue="cli"
31-
values={[
32-
{ label: '.NET CLI', value: 'cli' },
33-
{ label: 'Package Manager', value: 'nuget' },
34-
{ label: 'Package Reference', value: 'reference' }
35-
]}>
36-
<TabItem value="cli">
37-
<CodeBlock metastring="shell" className="shell">
38-
{installCodeCLI}
39-
</CodeBlock>
40-
</TabItem>
41-
<TabItem value="nuget">
42-
<CodeBlock metastring="shell" className="shell">
43-
{installCodeNuget}
44-
</CodeBlock>
45-
</TabItem>
46-
<TabItem value="reference">
47-
<CodeBlock metastring="xml" className="xml">
48-
{installReference}
49-
</CodeBlock>
50-
</TabItem>
51-
</Tabs>
52-
</div>);
53-
}
25+
return (
26+
<div style={{ padding: '30px' }}>
27+
<Tabs
28+
defaultValue="cli"
29+
values={[
30+
{ label: '.NET CLI', value: 'cli' },
31+
{ label: 'Package Manager', value: 'nuget' },
32+
{ label: 'Package Reference', value: 'reference' },
33+
]}
34+
>
35+
<TabItem value="cli">
36+
<CodeBlock metastring="shell" className="shell">
37+
{installCodeCLI}
38+
</CodeBlock>
39+
</TabItem>
40+
<TabItem value="nuget">
41+
<CodeBlock metastring="shell" className="shell">
42+
{installCodeNuget}
43+
</CodeBlock>
44+
</TabItem>
45+
<TabItem value="reference">
46+
<CodeBlock metastring="xml" className="xml">
47+
{installReference}
48+
</CodeBlock>
49+
</TabItem>
50+
</Tabs>
51+
</div>
52+
);
53+
};
5454

5555
export default InstallInstructions;

SpotifyAPI.Docs/src/pages/index.js

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import React from 'react';
2-
import classnames from 'classnames';
3-
import Layout from '@theme/Layout';
4-
import CodeBlock from '@theme/CodeBlock'
5-
import Tabs from '@theme/Tabs'
6-
import TabItem from '@theme/TabItem'
71
import Link from '@docusaurus/Link';
8-
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
92
import useBaseUrl from '@docusaurus/useBaseUrl';
10-
import styles from './styles.module.css';
11-
import GitHubButton from 'react-github-btn'
3+
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
4+
import CodeBlock from '@theme/CodeBlock';
5+
import Layout from '@theme/Layout';
6+
import TabItem from '@theme/TabItem';
7+
import Tabs from '@theme/Tabs';
8+
import classnames from 'classnames';
9+
import React from 'react';
10+
import GitHubButton from 'react-github-btn';
11+
1212
import InstallInstructions from '../install_instructions';
13+
import styles from './styles.module.css';
1314

14-
const exampleCode =
15-
`var spotify = new SpotifyClient("YourAccessToken");
15+
const exampleCode = `var spotify = new SpotifyClient("YourAccessToken");
1616
1717
var me = await spotify.UserProfile.Current();
1818
Console.WriteLine($"Hello there {me.DisplayName}");
@@ -30,7 +30,9 @@ const features = [
3030
imageUrl: 'img/undraw_preferences_uuo2.svg',
3131
description: () => (
3232
<>
33-
<code>SpotifyAPI-NET</code> allows you to quickly integrate with Spotify's Web API by supplying sane configuration defaults from the start. Later on, behaviour can be customized using extensive configuration possibilities.
33+
<code>SpotifyAPI-NET</code> allows you to quickly integrate with Spotify's Web API by supplying sane
34+
configuration defaults from the start. Later on, behaviour can be customized using extensive configuration
35+
possibilities.
3436
</>
3537
),
3638
},
@@ -39,7 +41,8 @@ const features = [
3941
imageUrl: 'img/undraw_project_completed_w0oq.svg',
4042
description: () => (
4143
<>
42-
The Spotify Web API consists of over 74 API calls. <code>SpotifyAPI-NET</code> provides fully typed requests/responses for all of them.
44+
The Spotify Web API consists of over 74 API calls. <code>SpotifyAPI-NET</code> provides fully typed
45+
requests/responses for all of them.
4346
</>
4447
),
4548
},
@@ -48,7 +51,8 @@ const features = [
4851
imageUrl: 'img/undraw_Devices_e67q.svg',
4952
description: () => (
5053
<>
51-
With the support of .NET Standard 2.X, <code>SpotifyAPI-NET</code> runs on many platforms, including .NET Core, UWP and Xamarin.Forms (Windows, Android, iOS and Mac)
54+
With the support of .NET Standard 2.X, <code>SpotifyAPI-NET</code> runs on many platforms, including .NET Core,
55+
UWP and Xamarin.Forms (Windows, Android, iOS and Mac)
5256
</>
5357
),
5458
},
@@ -57,7 +61,8 @@ const features = [
5761
imageUrl: 'img/undraw_QA_engineers_dg5p.svg',
5862
description: () => (
5963
<>
60-
<code>SpotifyAPI-NET</code> is built on a modular structure, which allows easy testing through mocks and stubs. Learn more by visiting the <Link to={useBaseUrl('docs/next/testing')}>Testing Guide</Link>
64+
<code>SpotifyAPI-NET</code> is built on a modular structure, which allows easy testing through mocks and stubs.
65+
Learn more by visiting the <Link to={useBaseUrl('docs/next/testing')}>Testing Guide</Link>
6166
</>
6267
),
6368
},
@@ -82,9 +87,7 @@ function Home() {
8287
const context = useDocusaurusContext();
8388
const { siteConfig = {} } = context;
8489
return (
85-
<Layout
86-
title={`${siteConfig.title}`}
87-
description="Documentation for the C# .NET SpotifyAPI-NET Library">
90+
<Layout title={`${siteConfig.title}`} description="Documentation for the C# .NET SpotifyAPI-NET Library">
8891
<header className={classnames('hero hero--primary', styles.heroBanner)}>
8992
<div className="container">
9093
<div className="row">
@@ -98,29 +101,31 @@ function Home() {
98101
data-icon="octicon-star"
99102
data-size="large"
100103
data-show-count="true"
101-
aria-label="Star JohnnyCrazy/SpotifyAPI-NET on GitHub">Star</GitHubButton>
104+
aria-label="Star JohnnyCrazy/SpotifyAPI-NET on GitHub"
105+
>
106+
Star
107+
</GitHubButton>
102108
<br />
103109
<a href="https://www.nuget.org/packages/SpotifyAPI.Web/" rel="noopener noreferrer">
104110
<img
105111
alt="Nuget"
106-
src="https://img.shields.io/nuget/vpre/SpotifyAPI.Web?label=SpotifyAPI.Web&style=flat-square">
107-
</img>{' '}
112+
src="https://img.shields.io/nuget/vpre/SpotifyAPI.Web?label=SpotifyAPI.Web&style=flat-square"
113+
></img>
114+
{' '}
108115
</a>
109116
<a href="https://www.nuget.org/packages/SpotifyAPI.Web.Auth/" rel="noopener noreferrer">
110117
<img
111118
alt="Nuget"
112-
src="https://img.shields.io/nuget/vpre/SpotifyAPI.Web.Auth?label=SpotifyAPI.Web.Auth&style=flat-square">
113-
</img>
119+
src="https://img.shields.io/nuget/vpre/SpotifyAPI.Web.Auth?label=SpotifyAPI.Web.Auth&style=flat-square"
120+
></img>
114121
</a>
115122
</h1>
116123
<p className="hero__subtitle">{siteConfig.tagline}</p>
117124
<div className={styles.buttons}>
118125
<Link
119-
className={classnames(
120-
'button button--outline button--secondary button--lg',
121-
styles.getStarted,
122-
)}
123-
to={useBaseUrl('docs/next/introduction')}>
126+
className={classnames('button button--outline button--secondary button--lg', styles.getStarted)}
127+
to={useBaseUrl('docs/next/introduction')}
128+
>
124129
Get Started
125130
</Link>
126131
</div>
957 Bytes
Loading

0 commit comments

Comments
 (0)