Skip to content

Commit 968ddd5

Browse files
committed
added pkce
1 parent 90876ba commit 968ddd5

File tree

1 file changed

+55
-33
lines changed

1 file changed

+55
-33
lines changed

launcher-csharp/Startup.cs

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -158,53 +158,48 @@ public void ConfigureServices(IServiceCollection services)
158158
options.TokenEndpoint = this.Configuration["DocuSign:TokenEndpoint"];
159159
options.UserInformationEndpoint = this.Configuration["DocuSign:UserInformationEndpoint"];
160160

161-
foreach (var apiType in this.apiTypes)
162-
{
163-
foreach (var scope in apiType.Value)
164-
{
165-
if (!options.Scope.Contains(scope.ToLower()))
166-
{
167-
options.Scope.Add(scope);
168-
}
169-
}
170-
}
161+
string codeVerifier = GenerateCodeVerifier();
162+
string codeChallenge = GenerateCodeChallenge(codeVerifier);
171163

172-
options.SaveTokens = true;
173-
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
174-
options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
175-
options.ClaimActions.MapJsonKey("accounts", "accounts");
176-
options.ClaimActions.MapCustomJson("account_id", obj => this.ExtractDefaultAccountValue(obj, "account_id"));
177-
options.ClaimActions.MapCustomJson("account_name", obj => this.ExtractDefaultAccountValue(obj, "account_name"));
178-
options.ClaimActions.MapCustomJson("base_uri", obj => this.ExtractDefaultAccountValue(obj, "base_uri"));
179-
options.ClaimActions.MapJsonKey("access_token", "access_token");
180-
options.ClaimActions.MapJsonKey("refresh_token", "refresh_token");
181-
options.ClaimActions.MapJsonKey("expires_in", "expires_in");
182164
options.Events = new OAuthEvents
183165
{
184166
OnRedirectToAuthorizationEndpoint = redirectContext =>
185167
{
186168
List<string> scopesForCurrentApi = this.apiTypes.GetValueOrDefault(Enum.Parse<ExamplesApiType>(this.Configuration["API"]));
187169

188-
redirectContext.RedirectUri = this.UpdateRedirectUriScopes(redirectContext.RedirectUri, scopesForCurrentApi);
170+
var redirectUri = this.UpdateRedirectUriScopes(redirectContext.RedirectUri, scopesForCurrentApi);
171+
172+
var pkceQuery = $"&code_challenge={codeChallenge}&code_challenge_method=S256";
173+
redirectContext.RedirectUri = redirectUri + pkceQuery;
174+
175+
redirectContext.HttpContext.Session.SetString("code_verifier", codeVerifier);
189176

190177
redirectContext.HttpContext.Response.Redirect(redirectContext.RedirectUri);
191178
return Task.FromResult(0);
192179
},
193180
OnCreatingTicket = async context =>
194181
{
195-
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
196-
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
197-
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
198-
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
199-
response.EnsureSuccessStatusCode();
200-
var user = JObject.Parse(await response.Content.ReadAsStringAsync());
201-
user.Add("access_token", context.AccessToken);
202-
user.Add("refresh_token", context.RefreshToken);
203-
user.Add("expires_in", DateTime.Now.Add(context.ExpiresIn.Value).ToString());
204-
using (JsonDocument payload = JsonDocument.Parse(user.ToString()))
182+
string codeVerifier = context.HttpContext.Session.GetString("code_verifier");
183+
184+
var tokenRequestParams = new Dictionary<string, string>
205185
{
206-
context.RunClaimActions(payload.RootElement);
207-
}
186+
{ "grant_type", "authorization_code" },
187+
{ "code", context.ProtocolMessage.Code },
188+
{ "redirect_uri", context.Properties.RedirectUri },
189+
{ "client_id", options.ClientId },
190+
{ "code_verifier", codeVerifier },
191+
};
192+
193+
var requestContent = new FormUrlEncodedContent(tokenRequestParams);
194+
var requestMessage = new HttpRequestMessage(HttpMethod.Post, options.TokenEndpoint)
195+
{
196+
Content = requestContent
197+
};
198+
var response = await context.Backchannel.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
199+
response.EnsureSuccessStatusCode();
200+
201+
var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
202+
context.RunClaimActions(payload.RootElement);
208203
},
209204
OnRemoteFailure = context =>
210205
{
@@ -266,6 +261,33 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
266261
});
267262
}
268263

264+
private string GenerateCodeVerifier()
265+
{
266+
using (var rng = new RNGCryptoServiceProvider())
267+
{
268+
var bytes = new byte[32];
269+
rng.GetBytes(bytes);
270+
return Base64UrlEncode(bytes);
271+
}
272+
}
273+
274+
private string GenerateCodeChallenge(string codeVerifier)
275+
{
276+
using (var sha256 = SHA256.Create())
277+
{
278+
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
279+
return Base64UrlEncode(hash);
280+
}
281+
}
282+
283+
private string Base64UrlEncode(byte[] input)
284+
{
285+
return Convert.ToBase64String(input)
286+
.Replace("+", "-")
287+
.Replace("/", "_")
288+
.Replace("=", "");
289+
}
290+
269291
private string UpdateRedirectUriScopes(string uri, List<string> wantedScopes)
270292
{
271293
const string pattern = @"(?:&|\?)scope=([^&]+)";

0 commit comments

Comments
 (0)