/
GoogleHandler.cs
116 lines (102 loc) · 5.03 KB
/
GoogleHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication.Google
{
/// <summary>
/// Authentication handler for Google's OAuth based authentication.
/// </summary>
public class GoogleHandler : OAuthHandler<GoogleOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="GoogleHandler"/>.
/// </summary>
/// <inheritdoc />
public GoogleHandler(IOptionsMonitor<GoogleOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
/// <inheritdoc />
protected override async Task<AuthenticationTicket> CreateTicketAsync(
ClaimsIdentity identity,
AuthenticationProperties properties,
OAuthTokenResponse tokens)
{
// Get the Google user
var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
var response = await Backchannel.SendAsync(request, Context.RequestAborted);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"An error occurred when retrieving Google user information ({response.StatusCode}). Please check if the authentication information is correct.");
}
using (var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()))
{
var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
context.RunClaimActions();
await Events.CreatingTicket(context);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
}
/// <inheritdoc />
protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
{
// Google Identity Platform Manual:
// https://developers.google.com/identity/protocols/OAuth2WebServer
var queryStrings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
queryStrings.Add("response_type", "code");
queryStrings.Add("client_id", Options.ClientId);
queryStrings.Add("redirect_uri", redirectUri);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.ScopeKey, FormatScope, Options.Scope);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.AccessTypeKey, Options.AccessType);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.ApprovalPromptKey);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.PromptParameterKey);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.LoginHintKey);
AddQueryString(queryStrings, properties, GoogleChallengeProperties.IncludeGrantedScopesKey, v => v?.ToString().ToLower(), (bool?)null);
var state = Options.StateDataFormat.Protect(properties);
queryStrings.Add("state", state);
var authorizationEndpoint = QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, queryStrings);
return authorizationEndpoint;
}
private void AddQueryString<T>(
IDictionary<string, string> queryStrings,
AuthenticationProperties properties,
string name,
Func<T, string> formatter,
T defaultValue)
{
string value = null;
var parameterValue = properties.GetParameter<T>(name);
if (parameterValue != null)
{
value = formatter(parameterValue);
}
else if (!properties.Items.TryGetValue(name, out value))
{
value = formatter(defaultValue);
}
// Remove the parameter from AuthenticationProperties so it won't be serialized into the state
properties.Items.Remove(name);
if (value != null)
{
queryStrings[name] = value;
}
}
private void AddQueryString(
IDictionary<string, string> queryStrings,
AuthenticationProperties properties,
string name,
string defaultValue = null)
=> AddQueryString(queryStrings, properties, name, x => x, defaultValue);
}
}