Asp.net mvc 将用户授权限制到我的Google域
应该可以将Google API OAuth2请求限制到特定的Google域。过去可以通过在请求的末尾&hd=mydomain.com进行黑客攻击。使用新的MVCAuth工具似乎不再可能了。你知道怎么做吗Asp.net mvc 将用户授权限制到我的Google域,asp.net-mvc,google-api,google-oauth,google-api-dotnet-client,Asp.net Mvc,Google Api,Google Oauth,Google Api Dotnet Client,应该可以将Google API OAuth2请求限制到特定的Google域。过去可以通过在请求的末尾&hd=mydomain.com进行黑客攻击。使用新的MVCAuth工具似乎不再可能了。你知道怎么做吗 public class AppFlowMetadata : FlowMetadata { private static readonly IAuthorizationCodeFlow flow = new AppGoogleAuthorizat
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new AppGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "***.apps.googleusercontent.com",
ClientSecret = "******"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore(HttpContext.Current.Server.MapPath("~/App_Data"), true) ,
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authorizeUri = new Uri(AuthorizationServerUrl).AddQuery("hd", "ourgoogledomain.com"); //is not in the request
var authUrl = new GoogleAuthorizationCodeRequestUrl(authorizeUri)
{
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
//AccessType = "offline",
// ApprovalPrompt = "force"
};
return authUrl;
}
}
下载了源代码后,我可以很容易地对请求对象进行子类化,并添加自定义参数:
public class GoogleDomainAuthorizationCodeRequestUrl : GoogleAuthorizationCodeRequestUrl
{
/// <summary>
/// Gets or sets the hosted domain.
/// When you want to limit authorizing users from a specific domain
/// </summary>
[Google.Apis.Util.RequestParameterAttribute("hd", Google.Apis.Util.RequestParameterType.Query)]
public string Hd { get; set; }
public GoogleDomainAuthorizationCodeRequestUrl(Uri authorizationServerUrl) : base(authorizationServerUrl)
{
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authUrl = new GoogleDomainAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
Hd = "mydomain.com",
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri
};
return authUrl;
}
}
下载了源代码后,我可以很容易地对请求对象进行子类化,并添加自定义参数:
public class GoogleDomainAuthorizationCodeRequestUrl : GoogleAuthorizationCodeRequestUrl
{
/// <summary>
/// Gets or sets the hosted domain.
/// When you want to limit authorizing users from a specific domain
/// </summary>
[Google.Apis.Util.RequestParameterAttribute("hd", Google.Apis.Util.RequestParameterType.Query)]
public string Hd { get; set; }
public GoogleDomainAuthorizationCodeRequestUrl(Uri authorizationServerUrl) : base(authorizationServerUrl)
{
}
}
public class AppGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow
{
public AppGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base(initializer) { }
public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(String redirectUri)
{
var authUrl = new GoogleDomainAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl))
{
Hd = "mydomain.com",
ClientId = ClientSecrets.ClientId,
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri
};
return authUrl;
}
}
传递hd参数确实是将用户限制到您的域的正确方法。但是,验证用户是否确实属于该托管域是很重要的。我在您的示例中看到,您已经了解了如何将此参数添加回您的请求中,因此我将介绍本文的第二部分
问题是用户实际上可以在客户端修改请求的URL并删除hd参数!因此,虽然传递此参数可以为用户提供最佳UI,但您还需要验证经过身份验证的用户是否确实属于该域
若要查看用户所属的托管Google Apps for Work domain,必须在授权范围列表中包含电子邮件。然后,执行以下操作之一:
备选案文1。验证ID令牌。
当您将代码交换为访问令牌时,令牌端点还将在ID_token参数中返回一个ID令牌,假设您在请求(如电子邮件)中包含一个标识作用域。如果用户是托管域的一部分,将出现hd声明,您应该检查它是否存在,并与您期望的内容相匹配
您可以在Google上阅读更多关于ID令牌的信息,包括一些指向示例代码和库的链接,以帮助您解码它们。可以在测试期间解码ID令牌
备选案文2。呼叫用户信息
获得OAuth访问令牌后,执行GET请求https://www.googleapis.com/plus/v1/people/me/openIdConnect 在标头中使用访问令牌。它将返回一个关于用户声明的JSON字典。如果用户是托管域的一部分,将出现hd声明,您应该检查它是否存在,并与您期望的内容相匹配
阅读更多的文章
选项1和选项2之间的主要区别在于,使用ID令牌,您可以避免到服务器的另一个HTTP往返,从而使其速度更快,更不容易出错。您可以使用交互方式尝试这两个选项。传递hd参数确实是将用户限制到您的域的正确方法。但是,验证用户是否确实属于该托管域是很重要的。我在您的示例中看到,您已经了解了如何将此参数添加回您的请求中,因此我将介绍本文的第二部分
问题是用户实际上可以在客户端修改请求的URL并删除hd参数!因此,虽然传递此参数可以为用户提供最佳UI,但您还需要验证经过身份验证的用户是否确实属于该域
若要查看用户所属的托管Google Apps for Work domain,必须在授权范围列表中包含电子邮件。然后,执行以下操作之一:
备选案文1。验证ID令牌。
当您将代码交换为访问令牌时,令牌端点还将在ID_token参数中返回一个ID令牌,假设您在请求(如电子邮件)中包含一个标识作用域。如果用户是托管域的一部分,将出现hd声明,您应该检查它是否存在,并与您期望的内容相匹配
您可以在Google上阅读更多关于ID令牌的信息,包括一些指向示例代码和库的链接,以帮助您解码它们。可以在测试期间解码ID令牌
备选案文2。呼叫用户信息
获得OAuth访问令牌后,执行GET请求https://www.googleapis.com/plus/v1/people/me/openIdConnect 在标头中使用访问令牌。它将返回一个关于用户声明的JSON字典。如果用户是托管域的一部分,将出现hd声明,您应该检查它是否存在,并与您期望的内容相匹配
阅读更多的文章
选项1和选项2之间的主要区别在于,使用ID令牌,您可以避免到服务器的另一个HTTP往返,从而使其速度更快,更不容易出错。您可以使用.@AMH以交互方式尝试这两个选项,要以最简单的方式进行,您应该创建自己的Google Provider,重写方法ApplyRedirect,并将附加参数(如hd)附加到address,该地址将用于重定向到新的Google auth页面:
public class GoogleAuthProvider : GoogleOAuth2AuthenticationProvider
{
public override void ApplyRedirect(GoogleOAuth2ApplyRedirectContext context)
{
var newRedirectUri = context.RedirectUri;
newRedirectUri += string.Format("&hd={0}", "your_domain.com");
context.Response.Redirect(newRedirectUri);
}
}
之后,只需将新提供商链接到您的选项:
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "your id",
ClientSecret = "your secret",
Provider = new GoogleAuthProvider(),
});
@AMH,要以最简单的方式执行此操作,您应该创建自己的Google Provider,重写方法ApplyRedirect,并将附加参数(如hd)附加到address,该参数将用于重定向到新的Google auth页面:
public class GoogleAuthProvider : GoogleOAuth2AuthenticationProvider
{
public override void ApplyRedirect(GoogleOAuth2ApplyRedirectContext context)
{
var newRedirectUri = context.RedirectUri;
newRedirectUri += string.Format("&hd={0}", "your_domain.com");
context.Response.Redirect(newRedirectUri);
}
}
之后,只需将新提供商链接到您的选项:
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "your id",
ClientSecret = "your secret",
Provider = new GoogleAuthProvider(),
});
与upda
特德。以前的答案不再适用。幸运的是,在新的实现中,有一种方法可以钩住身份验证事件来执行这样的任务
你需要一个类来处理两个事件——一个是在你去谷歌之前触发的,另一个是回来时触发的。首先,您限制可用于登录的域,然后,您确保具有正确域的电子邮件实际上用于登录:
internal class GoogleAuthEvents : OAuthEvents
{
private string _domainName;
public GoogleAuthEvents(string domainName)
{
this._domainName = domainName?.ToLower() ?? throw new ArgumentNullException(nameof(domainName));
}
public override Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context)
{
return base.RedirectToAuthorizationEndpoint(new OAuthRedirectToAuthorizationContext(
context.HttpContext,
context.Options,
context.Properties,
$"{context.RedirectUri}&hd={_domainName}"));
}
public override Task TicketReceived(TicketReceivedContext context)
{
var emailClaim = context.Ticket.Principal.Claims.FirstOrDefault(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
if (emailClaim == null || !emailClaim.Value.ToLower().EndsWith(_domainName))
{
context.Response.StatusCode = 403; // or redirect somewhere
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
然后需要通过GoogleOptions类将此事件处理程序传递给中间件:
与更新的。以前的答案不再适用。幸运的是,在新的实现中,有一种方法可以钩住身份验证事件来执行这样的任务
你需要一个类来处理两个事件——一个是在你去谷歌之前触发的,另一个是回来时触发的。首先,您限制可用于登录的域,然后,您确保具有正确域的电子邮件实际上用于登录:
internal class GoogleAuthEvents : OAuthEvents
{
private string _domainName;
public GoogleAuthEvents(string domainName)
{
this._domainName = domainName?.ToLower() ?? throw new ArgumentNullException(nameof(domainName));
}
public override Task RedirectToAuthorizationEndpoint(OAuthRedirectToAuthorizationContext context)
{
return base.RedirectToAuthorizationEndpoint(new OAuthRedirectToAuthorizationContext(
context.HttpContext,
context.Options,
context.Properties,
$"{context.RedirectUri}&hd={_domainName}"));
}
public override Task TicketReceived(TicketReceivedContext context)
{
var emailClaim = context.Ticket.Principal.Claims.FirstOrDefault(
c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
if (emailClaim == null || !emailClaim.Value.ToLower().EndsWith(_domainName))
{
context.Response.StatusCode = 403; // or redirect somewhere
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
然后需要通过GoogleOptions类将此事件处理程序传递给中间件:
我在搜索一个解决方案时发现了这篇文章,该解决方案使用OpenID指定托管域,并将其连接到Google。我能够使用这个包和下面的代码使它工作 在Startup.cs中
services.AddGoogleOpenIdConnect(options =>
{
options.ClientId = "*****";
options.ClientSecret = "*****";
options.SaveTokens = true;
options.EventsType = typeof(GoogleAuthenticationEvents);
});
services.AddTransient(provider => new GoogleAuthenticationEvents("example.com"));
别忘了app.UseAuthentication;在Startup.cs的配置方法中
然后是身份验证事件类
public class GoogleAuthenticationEvents : OpenIdConnectEvents
{
private readonly string _hostedDomain;
public GoogleAuthenticationEvents(string hostedDomain)
{
_hostedDomain = hostedDomain;
}
public override Task RedirectToIdentityProvider(RedirectContext context)
{
context.ProtocolMessage.Parameters.Add("hd", _hostedDomain);
return base.RedirectToIdentityProvider(context);
}
public override Task TicketReceived(TicketReceivedContext context)
{
var email = context.Principal.FindFirstValue(ClaimTypes.Email);
if (email == null || !email.ToLower().EndsWith(_hostedDomain))
{
context.Response.StatusCode = 403;
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
我在搜索一个解决方案时发现了这篇文章,该解决方案使用OpenID指定托管域,并将其连接到Google。我能够使用这个包和下面的代码使它工作 在Startup.cs中
services.AddGoogleOpenIdConnect(options =>
{
options.ClientId = "*****";
options.ClientSecret = "*****";
options.SaveTokens = true;
options.EventsType = typeof(GoogleAuthenticationEvents);
});
services.AddTransient(provider => new GoogleAuthenticationEvents("example.com"));
别忘了app.UseAuthentication;在Startup.cs的配置方法中
然后是身份验证事件类
public class GoogleAuthenticationEvents : OpenIdConnectEvents
{
private readonly string _hostedDomain;
public GoogleAuthenticationEvents(string hostedDomain)
{
_hostedDomain = hostedDomain;
}
public override Task RedirectToIdentityProvider(RedirectContext context)
{
context.ProtocolMessage.Parameters.Add("hd", _hostedDomain);
return base.RedirectToIdentityProvider(context);
}
public override Task TicketReceived(TicketReceivedContext context)
{
var email = context.Principal.FindFirstValue(ClaimTypes.Email);
if (email == null || !email.ToLower().EndsWith(_hostedDomain))
{
context.Response.StatusCode = 403;
context.HandleResponse();
}
return base.TicketReceived(context);
}
}
您可以附加用于hd参数的文档吗?您是否尝试将&hd=mydomain.com附加到authUrl?@abraham,它是一个在其构造函数中接受Uri的对象请参见代码。我已将参数添加到此Uri。但是,对象必须重建url并删除参数。如果我在浏览器中更改一次url并重新添加它,它将按预期工作。您可以附加用于hd参数的文档吗?您是否尝试将&hd=mydomain.com附加到authUrl?@abraham,它是一个在其构造函数中接受Uri的对象请参见代码。我已将参数添加到此Uri。但是,对象必须重建url并删除参数。如果我在浏览器中更改一次url并重新添加,它将按预期工作。谢谢。非常有用的信息。谢谢。非常有用的信息。如何使用它请我使用类似app.UseGoogleAuthenticationnew GoogleOAuth2AuthenticationOptions{ClientId=ddddddd,ClientSecret=xxxxx};如何使用它请我使用类似app.UseGoogleAuthenticationnew GoogleOAuth2AuthenticationOptions{ClientId=ddddddd,ClientSecret=xxxxx};