From a6f2baa49225038ec695f7d31e30c7ec181cb35f Mon Sep 17 00:00:00 2001 From: Jonas Dellinger Date: Wed, 3 Jun 2020 23:57:28 +0200 Subject: [PATCH] More docs regarding auth --- SpotifyAPI.Docs/docs/auth_introduction.md | 16 ++- SpotifyAPI.Docs/docs/authorization_code.md | 104 +++++++++++++++++ SpotifyAPI.Docs/docs/client_credentials.md | 45 ++++++++ SpotifyAPI.Docs/docs/implicit_grant.md | 106 ++++++++++++++++++ SpotifyAPI.Docs/docs/unit_testing.md | 35 ++++++ SpotifyAPI.Docs/sidebars.js | 4 + .../static/img/auth_comparison.png | Bin 0 -> 15479 bytes .../static/img/auth_protocol_handlers.png | Bin 0 -> 23188 bytes .../Http/SimpleRetryHandlerTest.cs | 6 +- 9 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 SpotifyAPI.Docs/docs/authorization_code.md create mode 100644 SpotifyAPI.Docs/docs/client_credentials.md create mode 100644 SpotifyAPI.Docs/docs/implicit_grant.md create mode 100644 SpotifyAPI.Docs/docs/unit_testing.md create mode 100644 SpotifyAPI.Docs/static/img/auth_comparison.png create mode 100644 SpotifyAPI.Docs/static/img/auth_protocol_handlers.png diff --git a/SpotifyAPI.Docs/docs/auth_introduction.md b/SpotifyAPI.Docs/docs/auth_introduction.md index ac1a210b..e6283cac 100644 --- a/SpotifyAPI.Docs/docs/auth_introduction.md +++ b/SpotifyAPI.Docs/docs/auth_introduction.md @@ -3,4 +3,18 @@ id: auth_introduction title: Introduction --- -Hello +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: + +* OAuth2 +* [Spotify Authorization Flows](https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow) + +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`). + +Then, continue with the docs of the specific auth flows: + +* [Client Credentials](client_credentials) +* [Implicit Grant](implicit_grant) +* [Authorization Code](authorization_code) +* Token Swap + +![auth comparison](/img/auth_comparison.png) diff --git a/SpotifyAPI.Docs/docs/authorization_code.md b/SpotifyAPI.Docs/docs/authorization_code.md new file mode 100644 index 00000000..8c097921 --- /dev/null +++ b/SpotifyAPI.Docs/docs/authorization_code.md @@ -0,0 +1,104 @@ +--- +id: authorization_code +title: Authorization Code +--- + +> This flow is suitable for long-running applications in which the user grants permission only once. It provides an access token that can be refreshed. Since the token exchange involves sending your secret key, perform this on a secure location, like a backend service, and not from a client such as a browser or from a mobile app. + +## Existing Web-Server + +If you are already in control of a Web-Server (like `ASP.NET`), you can start the flow by generating a login uri + +```csharp +// Make sure "http://localhost:5000" is in your applications redirect URIs! +var loginRequest = new LoginRequest( + new Uri("http://localhost:5000"), + "ClientId", + LoginRequest.ResponseType.Code +) +{ + Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative } +}; +var uri = loginRequest.ToUri(); +// Redirect user to uri via your favorite web-server +``` + +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` and a `code` parameter is attached to the query. This `code` has to be exchanged for an `access_token` and `refresh_token`: + +```csharp +// This method should be called from your web-server when the user visits "http://localhost:5000" +public Task GetCallback(string code) +{ + var response = await new OAuthClient().RequestToken( + new AuthorizationCodeTokenRequest("ClientId", "ClientSecret", code, "http://localhost:5000") + ); + + var spotify = new SpotifyClient(response.AccessToken); + // Also important for later: response.RefreshToken +} +``` + +If the token expires at some point (check via `response.IsExpired`), you can refresh it: + +```csharp + var newResponse = await new OAuthClient().RequestToken( + new AuthorizationCodeRefreshRequest("ClientId", "ClientSecret", response.RefreshToken) + ); + + var spotify = new SpotifyClient(newResponse.AccessToken); +``` + +You can also let the `AuthorizationCodeAuthenticator` take care of the refresh part: + +```csharp +var response = await new OAuthClient().RequestToken( + new AuthorizationCodeTokenRequest("ClientId", "ClientSecret", code, "http://localhost:5000") +); +var config = SpotifyClientConfig + .CreateDefault() + .WithAuthenticator(new AuthorizationCodeAuthenticator("ClientId", "ClientSecret", response)); + +var spotify = new SpotifyClient(config); +``` + +## Using Spotify.Web.Auth + +For cross-platform CLI and desktop apps (non `UWP` apps), `Spotify.Web.Auth` can be used to supply a small embedded Web Server for the code retrieval. + +:::warning +You're client secret will be exposed when embedded in a desktop/cli app. This can be abused and is not preffered. If possible, let the user create an application in spotify dashboard or let a server handle the spotify communication. +::: + +```csharp +private static EmbedIOAuthServer _server; + +public static async Task Main() +{ + // Make sure "http://localhost:5000/callback" is in your spotify application as redirect uri! + _server = new EmbedIOAuthServer(new Uri("http://localhost:5000/callback"), 5000); + await _server.Start(); + + _server.AuthorizationCodeReceived += OnAuthorizationCodeReceived; + + var request = new LoginRequest(_server.BaseUri, "ClientId", LoginRequest.ResponseType.Code) + { + Scope = new List { Scopes.UserReadEmail } + }; + BrowserUtil.Open(uri); +} + +private static async Task OnAuthorizationCodeReceived(object sender, AuthorizationCodeResponse response) +{ + await _server.Stop(); + + var config = SpotifyClientConfig.CreateDefault(); + var tokenResponse = await new OAuthClient(config).RequestToken( + new AuthorizationCodeTokenRequest( + "ClientId", "ClientSecret", response.Code, "http://localhost:5000/callback" + ) + ); + + var spotify = new SpotifyClient(tokenResponse.AccessToken); + // do calls with spotify and save token? +} +``` diff --git a/SpotifyAPI.Docs/docs/client_credentials.md b/SpotifyAPI.Docs/docs/client_credentials.md new file mode 100644 index 00000000..32847982 --- /dev/null +++ b/SpotifyAPI.Docs/docs/client_credentials.md @@ -0,0 +1,45 @@ +--- +id: client_credentials +title: Client Credentials +--- + +> The Client Credentials flow is used in server-to-server authentication. +> Only endpoints that do not access user information can be accessed. + +By supplying your `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET`, you get an access token. + +## Request token once + +To request an access token, build a `ClientCredentialsRequest` and send it via `OAuthClient`. This access token will expire after some time and you need to repeat the process. + +```csharp +public static async Task Main() +{ + var config = SpotifyClientConfig.CreateDefault(); + + var request = new ClientCredentialsRequest("CLIENT_ID", "CLIENT_SECRET"); + var response = await new OAuthClient(config).RequestToken(request); + + var spotify = new SpotifyClient(config.WithToken(response.AccessToken)); +} +``` + +## Request Token On-Demand + +You can also use `CredentialsAuthenticator`, which will make sure the spotify instance will always have an up-to-date access token by automatically refreshing the token on-demand. + +```csharp +public static async Task Main() +{ + var config = SpotifyClientConfig + .CreateDefault() + .WithAuthenticator(new CredentialsAuthenticator("CLIENT_ID", "CLIENT_SECRET")); + + var spotify = new SpotifyClient(config); +} +``` + +:::info +There is no thread safety guaranteed when using `CredentialsAuthenticator`. +::: + diff --git a/SpotifyAPI.Docs/docs/implicit_grant.md b/SpotifyAPI.Docs/docs/implicit_grant.md new file mode 100644 index 00000000..707c66e4 --- /dev/null +++ b/SpotifyAPI.Docs/docs/implicit_grant.md @@ -0,0 +1,106 @@ +--- +id: implicit_grant +title: Implicit Grant +--- + +> Implicit grant flow is for clients that are implemented entirely using JavaScript and running in the resource owner’s browser. You do not need any server-side code to use it. Rate limits for requests are improved but there is no refresh token provided. This flow is described in RFC-6749. + +This flow is useful for getting a user access token for a short timespan + +## Existing Web-Server + +If you are already in control of a Web-Server (like `ASP.NET`), you can start the flow by generating a login uri + +```csharp +// Make sure "http://localhost:5000" is in your applications redirect URIs! +var loginRequest = new LoginRequest( + new Uri("http://localhost:5000"), + "ClientId", + LoginRequest.ResponseType.Token +) +{ + Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative } +}; +var uri = loginRequest.ToUri(); +// Redirect user to uri via your favorite web-server +``` + +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` and the fragment identifier (`#` part of URI) will contain an access token. + +:::warning +Note, this parameter is not sent to the server! You need JavaScript to access it. +::: + +## Using custom Protocols + +This flow can also be used with custom protocols instead of `http`/`https`. This is especially interesting for `UWP` apps, since your able to register custom protocol handlers quite easily. + +![protocol handlers](/img/auth_protocol_handlers.png) + +The process is very similar, you generate a uri and open it for the user + +```csharp +// Make sure "spotifyapi.web.oauth://token" is in your applications redirect URIs! +var loginRequest = new LoginRequest( + new Uri("spotifyapi.web.oauth://token"), + "ClientId", + LoginRequest.ResponseType.Token +) +{ + Scope = new[] { Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative } +}; +var uri = loginRequest.ToUri(); + +// This call requires Spotify.Web.Auth +BrowserUtil.Open(uri); +``` + +After the user logged in and consented your app, your `UWP` app will receive a callback: + +```csharp +protected override void OnActivated(IActivatedEventArgs args) +{ + if (args.Kind == ActivationKind.Protocol) + { + ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs; + var publisher = Mvx.IoCProvider.Resolve(); + + // This Uri contains your access token in the Fragment part + Console.WriteLine(eventArgs.Uri); + } +} +``` + +For a real example, have a look at the [UWP Example](https://github.com/JohnnyCrazy/SpotifyAPI-NET/tree/v6/SpotifyAPI.Web.Examples/Example.UWP) + +# Using Spotify.Web.Auth + +For cross-platform CLI and desktop apps (non `UWP` apps), custom protocol handlers are sometimes not an option. The fallback here is a small cross-platform embedded web server running on `http://localhost:5000` serving javascript. The javscript will parse the fragment part of the URI and sends a request to the web server in the background. The web server then notifies your appliciation via event. + +```csharp +private static EmbedIOAuthServer _server; + +public static async Task Main() +{ + // Make sure "http://localhost:5000/callback" is in your spotify application as redirect uri! + _server = new EmbedIOAuthServer(new Uri("http://localhost:5000/callback"), 5000); + await _server.Start(); + + _server.ImplictGrantReceived += OnImplictGrantReceived; + + var request = new LoginRequest(_server.BaseUri, "ClientId", LoginRequest.ResponseType.Code) + { + Scope = new List { Scopes.UserReadEmail } + }; + BrowserUtil.Open(uri); +} + +private static async Task OnImplictGrantReceived(object sender, ImplictGrantResponse response) +{ + await _server.Stop(); + var spotify = new SpotifyClient(response.AccessToken); + // do calls with spotify +} +``` + +For real examples, have a look at [Example.CLI.PersistentConfig](https://github.com/JohnnyCrazy/SpotifyAPI-NET/tree/v6/SpotifyAPI.Web.Examples/Example.CLI.PersistentConfig) and [Example.CLI.CustomHTML](https://github.com/JohnnyCrazy/SpotifyAPI-NET/tree/v6/SpotifyAPI.Web.Examples/Example.CLI.CustomHTML) diff --git a/SpotifyAPI.Docs/docs/unit_testing.md b/SpotifyAPI.Docs/docs/unit_testing.md new file mode 100644 index 00000000..f0da54a7 --- /dev/null +++ b/SpotifyAPI.Docs/docs/unit_testing.md @@ -0,0 +1,35 @@ +--- +id: unit_testing +title: Unit Testing +--- + +The modular structure of the library makes it easy to mock the API when unit testing. Consider the following method: + +```csharp +public static async Task IsAdmin(IUserProfileClient userProfileClient) +{ + // get loggedin user + var user = await userProfileClient.Current(); + + // only my user id is an admin + return user.Id == "1122095781"; +} +``` + +Using `Moq`, this can be tested without doing any network requests: + +```csharp +[Test] +public async Task IsAdmin_SuccessTest() +{ + var userProfileClient = new Mock(); + userProfileClient.Setup(u => u.Current()).Returns( + Task.FromResult(new PrivateUser + { + Id = "1122095781" + }) + ); + + Assert.AreEqual(true, await IsAdmin(userProfileClient.Object)); +} +``` diff --git a/SpotifyAPI.Docs/sidebars.js b/SpotifyAPI.Docs/sidebars.js index e3175973..f9d616f6 100644 --- a/SpotifyAPI.Docs/sidebars.js +++ b/SpotifyAPI.Docs/sidebars.js @@ -13,6 +13,7 @@ module.exports = { 'proxy', 'pagination', 'retry_handling', + 'unit_testing' ] }, { @@ -20,6 +21,9 @@ module.exports = { label: 'Authentication Guides', items: [ 'auth_introduction', + 'client_credentials', + 'implicit_grant', + 'authorization_code' ] }, { diff --git a/SpotifyAPI.Docs/static/img/auth_comparison.png b/SpotifyAPI.Docs/static/img/auth_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..60553ef5b99199c977c81dddfa10fe54ac974a1d GIT binary patch literal 15479 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 Pg`NJv?qrGO<;edAS-(}G literal 0 HcmV?d00001 diff --git a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs index d2d3550a..4e13f516 100644 --- a/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs +++ b/SpotifyAPI.Web.Tests/Http/SimpleRetryHandlerTest.cs @@ -36,7 +36,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(2, retryCalled); Assert.AreEqual(setup.Response.Object, response); - setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Exactly(2)); + setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Exactly(2)); } [Test] @@ -67,7 +67,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(1, retryCalled); Assert.AreEqual(successResponse.Object, response); - setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once); + setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Once); } [Test] @@ -97,7 +97,7 @@ namespace SpotifyAPI.Web Assert.AreEqual(1, retryCalled); Assert.AreEqual(successResponse.Object, response); - setup.Sleep.Verify(s => s(TimeSpan.FromMilliseconds(50)), Times.Once); + setup.Sleep.Verify(s => s(TimeSpan.FromSeconds(50)), Times.Once); } [Test]