diff --git a/AspNet.Security.OAuth.Providers.sln b/AspNet.Security.OAuth.Providers.sln index 19e75686e..360147bbb 100644 --- a/AspNet.Security.OAuth.Providers.sln +++ b/AspNet.Security.OAuth.Providers.sln @@ -209,6 +209,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C2CA4B38-A docs\reddit.md = docs\reddit.md docs\salesforce.md = docs\salesforce.md docs\sign-in-with-apple.md = docs\sign-in-with-apple.md + docs\snapchat.md = docs\snapchat.md docs\stackexchange.md = docs\stackexchange.md docs\superoffice.md = docs\superoffice.md docs\trakt.md = docs\trakt.md @@ -280,6 +281,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Naver EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.HubSpot", "src\AspNet.Security.OAuth.HubSpot\AspNet.Security.OAuth.HubSpot.csproj", "{23E576EB-6514-4617-8F04-FE7D5540136D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Snapchat", "src\AspNet.Security.OAuth.Snapchat\AspNet.Security.OAuth.Snapchat.csproj", "{ECD22287-9B9F-489A-84A7-E66D65A39D73}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -638,6 +641,10 @@ Global {23E576EB-6514-4617-8F04-FE7D5540136D}.Debug|Any CPU.Build.0 = Debug|Any CPU {23E576EB-6514-4617-8F04-FE7D5540136D}.Release|Any CPU.ActiveCfg = Release|Any CPU {23E576EB-6514-4617-8F04-FE7D5540136D}.Release|Any CPU.Build.0 = Release|Any CPU + {ECD22287-9B9F-489A-84A7-E66D65A39D73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECD22287-9B9F-489A-84A7-E66D65A39D73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECD22287-9B9F-489A-84A7-E66D65A39D73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECD22287-9B9F-489A-84A7-E66D65A39D73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -737,6 +744,7 @@ Global {FAE6E4C3-5A0D-45A5-9D17-57F4F6DA2593} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D} {289A91E9-81A9-422D-9CCD-12819081A29A} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D} {23E576EB-6514-4617-8F04-FE7D5540136D} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D} + {ECD22287-9B9F-489A-84A7-E66D65A39D73} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C} diff --git a/README.md b/README.md index 580a52bcd..92febdfc3 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ If a provider you're looking for does not exist, consider making a PR to add one | ServiceChannel | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.ServiceChannel?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.ServiceChannel/ "Download AspNet.Security.OAuth.ServiceChannel from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.ServiceChannel?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.ServiceChannel "Download AspNet.Security.OAuth.ServiceChannel from MyGet.org") | [Documentation](https://developer.servicechannel.com/basics/general/authentication "ServiceChannel developer documentation") | | Shopify | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Shopify?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Shopify/ "Download AspNet.Security.OAuth.Shopify from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Shopify?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Shopify "Download AspNet.Security.OAuth.Shopify from MyGet.org") | [Documentation](https://help.shopify.com/en/api/getting-started/authentication/oauth "Shopify developer documentation") | | Slack | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Slack?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Slack/ "Download AspNet.Security.OAuth.Slack from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Slack?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Slack "Download AspNet.Security.OAuth.Slack from MyGet.org") | [Documentation](https://api.slack.com/docs/oauth "Slack developer documentation") | +| Snapchat | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Snapchat?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Snapchat/ "Download AspNet.Security.OAuth.Snapchat from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Snapchat?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Snapchat "Download AspNet.Security.OAuth.Snapchat from MyGet.org") | [Documentation](https://marketingapi.snapchat.com/docs/#authentication "Snapchat developer documentation") | | SoundCloud | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.SoundCloud?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.SoundCloud/ "Download AspNet.Security.OAuth.SoundCloud from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.SoundCloud?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.SoundCloud "Download AspNet.Security.OAuth.SoundCloud from MyGet.org") | [Documentation](https://developers.soundcloud.com/docs#authentication "SoundCloud developer documentation") | | Spotify | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.Spotify?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.Spotify/ "Download AspNet.Security.OAuth.Spotify from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.Spotify?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Spotify "Download AspNet.Security.OAuth.Spotify from MyGet.org") | [Documentation](https://developer.spotify.com/documentation/general/guides/authorization-guide/ "Spotify developer documentation") | | Stack Exchange | [![NuGet](https://buildstats.info/nuget/AspNet.Security.OAuth.StackExchange?includePreReleases=false)](https://www.nuget.org/packages/AspNet.Security.OAuth.StackExchange/ "Download AspNet.Security.OAuth.StackExchange from NuGet.org") | [![MyGet](https://buildstats.info/myget/aspnet-contrib/AspNet.Security.OAuth.StackExchange?includePreReleases=true)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.StackExchange "Download AspNet.Security.OAuth.StackExchange from MyGet.org") | [Documentation](https://api.stackexchange.com/docs/authentication "Stack Exchange developer documentation") | diff --git a/docs/README.md b/docs/README.md index aacc9ea82..3e6680c8f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -63,6 +63,7 @@ covered by the section above. | QQ | _Optional_ | [Documentation](qq.md "QQ provider documentation") | | Reddit | _Optional_ | [Documentation](reddit.md "Reddit provider documentation") | | Salesforce | _Optional_ | [Documentation](salesforce.md "Salesforce provider documentation") | +| Snapchat | _Optional_ | [Documentation](snapchat.md "Snapchat provider documentation") | | StackExchange | _Optional_ | [Documentation](stackexchange.md "StackExchange provider documentation") | | SuperOffice | **Required** | [Documentation](superoffice.md "SuperOffice provider documentation") | | Trakt | _Optional_ | [Documentation](trakt.md "Trakt provider documentation") | diff --git a/docs/snapchat.md b/docs/snapchat.md new file mode 100644 index 000000000..39fb836eb --- /dev/null +++ b/docs/snapchat.md @@ -0,0 +1,25 @@ +# Integrating the Snapchat Provider + +## Example + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddAuthentication(options => /* Auth configuration */) + .AddSnapchat(options => + { + options.ClientId = "my-client-id"; + options.ClientSecret = "my-client-secret"; + }); +} + +public void Configure(IApplicationBuilder app) +{ + app.UseAuthentication(); + app.UseAuthorization(); +} +``` + +## Required Additional Settings + +_None._ diff --git a/src/AspNet.Security.OAuth.Snapchat/AspNet.Security.OAuth.Snapchat.csproj b/src/AspNet.Security.OAuth.Snapchat/AspNet.Security.OAuth.Snapchat.csproj new file mode 100644 index 000000000..254025f67 --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/AspNet.Security.OAuth.Snapchat.csproj @@ -0,0 +1,20 @@ + + + + $(DefaultNetCoreTargetFramework) + 6.0.8 + true + + + + ASP.NET Core security middleware enabling Snapchat authentication. + Nikola Maksimovic + aspnetcore;authentication;oauth;security;snapchat + + + + + + + + diff --git a/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationConstants.cs b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationConstants.cs new file mode 100644 index 000000000..0de1457c2 --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationConstants.cs @@ -0,0 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +namespace AspNet.Security.OAuth.Snapchat; + +/// +/// Contains constants specific to the . +/// +public static class SnapchatAuthenticationConstants +{ + public static class Claims + { + public const string TeamId = "organization_id"; + public const string MemberStatus = "member_status"; + public const string UserId = "id"; + } +} diff --git a/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationDefaults.cs b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationDefaults.cs new file mode 100644 index 000000000..28fc0e22f --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationDefaults.cs @@ -0,0 +1,49 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +namespace AspNet.Security.OAuth.Snapchat; + +/// +/// Default values used by the Snapchat authentication middleware. +/// +public static class SnapchatAuthenticationDefaults +{ + /// + /// Default value for . + /// + public const string AuthenticationScheme = "Snapchat"; + + /// + /// Default value for . + /// + public static readonly string DisplayName = "Snapchat"; + + /// + /// Default value for . + /// + public static readonly string Issuer = "Snapchat"; + + /// + /// Default value for . + /// + public static readonly string CallbackPath = "/signin-snapchat"; + + /// + /// Default value for . + /// + public static readonly string AuthorizationEndpoint = "https://accounts.snapchat.com/login/oauth2/authorize"; + + /// + /// Default value for . + /// + public static readonly string TokenEndpoint = "https://accounts.snapchat.com/login/oauth2/access_token"; + + /// + /// Default value for . + /// For more info about this endpoint, see https://marketingapi.snapchat.com/docs/#user. + /// + public static readonly string UserInformationEndpoint = "https://adsapi.snapchat.com/v1/me"; +} diff --git a/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationExtensions.cs b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationExtensions.cs new file mode 100644 index 000000000..a9592b29b --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationExtensions.cs @@ -0,0 +1,74 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +using AspNet.Security.OAuth.Snapchat; + +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// Extension methods to add Snapchat authentication capabilities to an HTTP application pipeline. +/// +public static class SnapchatAuthenticationExtensions +{ + /// + /// Adds to the specified + /// , which enables Snapchat authentication capabilities. + /// + /// The authentication builder. + /// The . + public static AuthenticationBuilder AddSnapchat([NotNull] this AuthenticationBuilder builder) + { + return builder.AddSnapchat(SnapchatAuthenticationDefaults.AuthenticationScheme, options => { }); + } + + /// + /// Adds to the specified + /// , which enables Snapchat authentication capabilities. + /// + /// The authentication builder. + /// The delegate used to configure the OpenID 2.0 options. + /// The . + public static AuthenticationBuilder AddSnapchat( + [NotNull] this AuthenticationBuilder builder, + [NotNull] Action configuration) + { + return builder.AddSnapchat(SnapchatAuthenticationDefaults.AuthenticationScheme, configuration); + } + + /// + /// Adds to the specified + /// , which enables Snapchat authentication capabilities. + /// + /// The authentication builder. + /// The authentication scheme associated with this instance. + /// The delegate used to configure the Snapchat options. + /// The . + public static AuthenticationBuilder AddSnapchat( + [NotNull] this AuthenticationBuilder builder, + [NotNull] string scheme, + [NotNull] Action configuration) + { + return builder.AddSnapchat(scheme, SnapchatAuthenticationDefaults.DisplayName, configuration); + } + + /// + /// Adds to the specified + /// , which enables Snapchat authentication capabilities. + /// + /// The authentication builder. + /// The authentication scheme associated with this instance. + /// The optional display name associated with this instance. + /// The delegate used to configure the Snapchat options. + /// The . + public static AuthenticationBuilder AddSnapchat( + [NotNull] this AuthenticationBuilder builder, + [NotNull] string scheme, + [CanBeNull] string caption, + [NotNull] Action configuration) + { + return builder.AddOAuth(scheme, caption, configuration); + } +} diff --git a/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationHandler.cs new file mode 100644 index 000000000..f24512b6d --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationHandler.cs @@ -0,0 +1,75 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace AspNet.Security.OAuth.Snapchat; + +public partial class SnapchatAuthenticationHandler : OAuthHandler +{ + public SnapchatAuthenticationHandler( + [NotNull] IOptionsMonitor options, + [NotNull] ILoggerFactory logger, + [NotNull] UrlEncoder encoder, + [NotNull] ISystemClock clock) + : base(options, logger, encoder, clock) + { + } + + protected override async Task CreateTicketAsync( + [NotNull] ClaimsIdentity identity, + [NotNull] AuthenticationProperties properties, + [NotNull] OAuthTokenResponse tokens) + { + string endpoint = Options.UserInformationEndpoint; + + using var request = new HttpRequestMessage(HttpMethod.Get, endpoint); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); + + using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); + if (!response.IsSuccessStatusCode) + { + await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted); + throw new HttpRequestException("An error occurred while retrieving the user profile from Snapchat."); + } + + using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted)); + + var principal = new ClaimsPrincipal(identity); + var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement); + context.RunClaimActions(); + + await Events.CreatingTicket(context); + return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name); + } + + private static partial class Log + { + internal static async Task UserProfileErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken) + { + UserProfileError( + logger, + response.StatusCode, + response.Headers.ToString(), + await response.Content.ReadAsStringAsync(cancellationToken)); + } + + [LoggerMessage(1, LogLevel.Error, "An error occurred while retrieving the user profile: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")] + private static partial void UserProfileError( + ILogger logger, + System.Net.HttpStatusCode status, + string headers, + string body); + } +} diff --git a/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationOptions.cs new file mode 100644 index 000000000..54499f25c --- /dev/null +++ b/src/AspNet.Security.OAuth.Snapchat/SnapchatAuthenticationOptions.cs @@ -0,0 +1,37 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using static AspNet.Security.OAuth.Snapchat.SnapchatAuthenticationConstants; + +namespace AspNet.Security.OAuth.Snapchat; + +/// +/// Defines a set of options used by . +/// +public class SnapchatAuthenticationOptions : OAuthOptions +{ + public SnapchatAuthenticationOptions() + { + ClaimsIssuer = SnapchatAuthenticationDefaults.Issuer; + + CallbackPath = SnapchatAuthenticationDefaults.CallbackPath; + + AuthorizationEndpoint = SnapchatAuthenticationDefaults.AuthorizationEndpoint; + TokenEndpoint = SnapchatAuthenticationDefaults.TokenEndpoint; + UserInformationEndpoint = SnapchatAuthenticationDefaults.UserInformationEndpoint; + + ClaimActions.MapJsonSubKey(ClaimTypes.NameIdentifier, "me", "id"); + ClaimActions.MapJsonSubKey(ClaimTypes.Name, "me", "display_name"); + ClaimActions.MapJsonSubKey(ClaimTypes.Email, "me", "email"); + ClaimActions.MapJsonSubKey(Claims.UserId, "me", "id"); + ClaimActions.MapJsonSubKey(Claims.TeamId, "me", "organization_id"); + ClaimActions.MapJsonSubKey(Claims.MemberStatus, "me", "member_status"); + Scope.Add("snapchat-marketing-api"); + } +} diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/SnapchatTests.cs b/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/SnapchatTests.cs new file mode 100644 index 000000000..ea67129bd --- /dev/null +++ b/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/SnapchatTests.cs @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers + * for more information concerning the license and the contributors participating to this project. + */ + +namespace AspNet.Security.OAuth.Snapchat; + +public class SnapchatTests : OAuthTests +{ + public SnapchatTests(ITestOutputHelper outputHelper) + { + OutputHelper = outputHelper; + } + + public override string DefaultScheme => SnapchatAuthenticationDefaults.AuthenticationScheme; + + protected internal override void RegisterAuthentication(AuthenticationBuilder builder) + { + builder.AddSnapchat(options => + { + ConfigureDefaults(builder, options); + }); + } + + [Theory] + [InlineData(ClaimTypes.NameIdentifier, "id")] + [InlineData(ClaimTypes.Name, "Test Testing")] + [InlineData(ClaimTypes.Email, "test.testing@testmail.com")] + [InlineData("member_status", "MEMBER")] + [InlineData("organization_id", "id")] + [InlineData("id", "id")] + public async Task Can_Sign_In_Using_Snapchat(string claimType, string claimValue) + { + // Arrange + using var server = CreateTestServer(); + + // Act + var claims = await AuthenticateUserAsync(server); + + // Assert + AssertClaim(claims, claimType, claimValue); + } +} diff --git a/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/bundle.json b/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/bundle.json new file mode 100644 index 000000000..79d00907d --- /dev/null +++ b/test/AspNet.Security.OAuth.Providers.Tests/Snapchat/bundle.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://raw.githubusercontent.com/justeat/httpclient-interception/master/src/HttpClientInterception/Bundles/http-request-bundle-schema.json", + "items": [ + { + "comment": "https://marketingapi.snapchat.com/docs/#generate-an-access-token", + "uri": "https://accounts.snapchat.com/login/oauth2/access_token", + "method": "POST", + "contentFormat": "json", + "contentJson": { + "access_token": "eyJpc3MiOiJodHRwczpcL1vwYWNbj3VudHMuc25hcGNoYXQuY29tXC9hY2NvdW50c1wvb2F1dGgyXC90b2tlbiIsInR5cCI6IkpXVCIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJhbGciOiJkaXIiLCJraWQiOiJhY2Nlc3MtdG9rZW4tYTEyOGNiYy1oczI1Ni4wIn0..kC0b5KF1kSnEkSAIkRtprg.7hIEPoPZbAQeLUg_98Mendu_gc3Dydsv47cmzErhQXh5B03NU0RzQJ6UsYlKLHlYCGyTfG94gNtA3S_R-5mAwZ4yvEQF7bk2wHH38nE9tujpYIFkwOitKBU8O5k_yz4-fUCqtKN_RFOIB04DPHBQZlWaNY_LPsnlkv2bOHuRLtP0JfCVejKEbHmzZIJkoeYG00wlPdYwtqNLgx-fgyT6AOsE1r0_nPmMUCWisfrchWMytO0LLvRAr-jPrv4WpxJ6mZ8--b_ZTD3tHOMg356nLZxSR5WeYi53LQLBphAKdQTlq90ru237wvJ5ui1f_yr5FtigTvNaRMc1ItKAy6zqjKlzUKKSqjpc4_lvyKFAKyRJUufZZZuPBRwN3i2t4ZQ95r28jatwvX_gPYd_0mbCCjI3LUyEhz7DaXLRqKvaakiMwQ2nZ_fqOq_NSW1k3NU1YzdN3sC2Zt1KCkIMRBoQ4BNm3URZ06agLd3aww6pOYs549MwHgT4FCCG_V3ph64CunkJASYdmLglacba881Ch8vLmO7RESFjOdmoC2mN6jg-inX3DvqpAztOjuWgBb0Valc9Bv88mwNYeTMofhICMNMoWS0Mset4r-8hADYwmAuVI5KWoswL3jwp6JNDL8e-adZWpYAQEN49xWGa7LcBxfhGN3plJWF9yA3zY47ELrkJOLXbcxicXqciKBplAa0L-t5hMYZI7PA7GofaWWY2MO_k5eQ6KLEGsPAIYaer0Zc.ExvLbrwcli1b1ck2WddwZA", + "scope": "snapchat-marketing-api", + "state": "wmKkg0TWgppW8PTBZ20sldUmF7hwvU" + } + }, + { + "comment": "https://marketingapi.snapchat.com/docs/#user", + "uri": "https://adsapi.snapchat.com/v1/me", + "contentFormat": "json", + "contentJson": { + "ok": true, + "me": { + "id": "id", + "updated_at": "2022-08-11T09:09:03.273Z", + "created_at": "2022-08-11T08:49:40.839Z", + "created_by_app_id": "id", + "created_by_user": "id", + "last_updated_by_app_id": "id", + "last_updated_by_user": "id", + "email": "test.testing@testmail.com", + "organization_id": "id", + "display_name": "Test Testing", + "snapchat_username": "ttesting", + "member_status": "MEMBER" + } + } + } + ] +}