使用 Google OAuth2.0将登录电子邮件限制为特定的域名

我似乎找不到任何文档说明如何限制我的 web 应用程序(使用 OAuth2.0和 Google API)的登录,只接受来自用户的认证请求,用户的电子邮件具有特定的域名或域名集。我想要白名单而不是黑名单。

有没有人对如何做到这一点有什么建议,有没有关于官方接受的这样做的方法的文档,或者有没有一个简单、安全的解决方案?

郑重声明,我不知道任何关于用户的信息,直到他们尝试通过 Google 的 OAuth 身份验证登录。所有我收到的回来是基本的用户信息和电子邮件。

73827 次浏览

So I've got an answer for you. In the OAuth request you can add hd=example.com and it will restrict authentication to users from that domain (I don't know if you can do multiple domains). You can find hd parameter documented here

I'm using the Google API libraries from here: http://code.google.com/p/google-api-php-client/wiki/OAuth2 so I had to manually edit the /auth/apiOAuth2.php file to this:

public function createAuthUrl($scope) {
$params = array(
'response_type=code',
'redirect_uri=' . urlencode($this->redirectUri),
'client_id=' . urlencode($this->clientId),
'scope=' . urlencode($scope),
'access_type=' . urlencode($this->accessType),
'approval_prompt=' . urlencode($this->approvalPrompt),
'hd=example.com'
);


if (isset($this->state)) {
$params[] = 'state=' . urlencode($this->state);
}
$params = implode('&', $params);
return self::OAUTH2_AUTH_URL . "?$params";
}

I'm still working on this app and found this, which may be the more correct answer to this question. https://developers.google.com/google-apps/profiles/

When defining your provider, pass in a hash at the end with the 'hd' parameter. You can read up on that here. https://developers.google.com/accounts/docs/OpenIDConnect#hd-param

E.g., for config/initializers/devise.rb

config.omniauth :google_oauth2, 'identifier', 'key', {hd: 'yourdomain.com'}

Here's what I did using passport in node.js. profile is the user attempting to log in.

//passed, stringified email login
var emailString = String(profile.emails[0].value);
//the domain you want to whitelist
var yourDomain = '@google.com';
//check the x amount of characters including and after @ symbol of passed user login.
//This means '@google.com' must be the final set of characters in the attempted login
var domain = emailString.substr(emailString.length - yourDomain.length);


//I send the user back to the login screen if domain does not match
if (domain != yourDomain)
return done(err);

Then just create logic to look for multiple domains instead of just one. I believe this method is secure because 1. the '@' symbol is not a valid character in the first or second part of an email address. I could not trick the function by creating an email address like mike@fake@google.com 2. In a traditional login system I could, but this email address could never exist in Google. If it's not a valid Google account, you can't login.

Client Side:

Using the auth2 init function, you can pass the hosted_domain parameter to restrict the accounts listed on the signin popup to those matching your hosted_domain. You can see this in the documentation here: https://developers.google.com/identity/sign-in/web/reference

Server Side:

Even with a restricted client-side list you will need to verify that the id_token matches the hosted domain you specified. For some implementations this means checking the hd attribute you receive from Google after verifying the token.

Full Stack Example:

Web Code:

gapi.load('auth2', function () {
// init auth2 with your hosted_domain
// only matching accounts will show up in the list or be accepted
var auth2 = gapi.auth2.init({
client_id: "your-client-id.apps.googleusercontent.com",
hosted_domain: 'your-special-domain.example'
});


// setup your signin button
auth2.attachClickHandler(yourButtonElement, {});


// when the current user changes
auth2.currentUser.listen(function (user) {
// if the user is signed in
if (user && user.isSignedIn()) {
// validate the token on your server,
// your server will need to double check that the
// `hd` matches your specified `hosted_domain`;
validateTokenOnYourServer(user.getAuthResponse().id_token)
.then(function () {
console.log('yay');
})
.catch(function (err) {
auth2.then(function() { auth2.signOut(); });
});
}
});
});

Server Code (using googles Node.js library):

If you're not using Node.js you can view other examples here: https://developers.google.com/identity/sign-in/web/backend-auth

const GoogleAuth = require('google-auth-library');
const Auth = new GoogleAuth();
const authData = JSON.parse(fs.readFileSync(your_auth_creds_json_file));
const oauth = new Auth.OAuth2(authData.web.client_id, authData.web.client_secret);


const acceptableISSs = new Set(
['accounts.google.com', 'https://accounts.google.com']
);


const validateToken = (token) => {
return new Promise((resolve, reject) => {
if (!token) {
reject();
}
oauth.verifyIdToken(token, null, (err, ticket) => {
if (err) {
return reject(err);
}
const payload = ticket.getPayload();
const tokenIsOK = payload &&
payload.aud === authData.web.client_id &&
new Date(payload.exp * 1000) > new Date() &&
acceptableISSs.has(payload.iss) &&
payload.hd === 'your-special-domain.example';
return tokenIsOK ? resolve() : reject();
});
});
};

Since 2015 there has been a function in the library to set this without needing to edit the source of the library as in the workaround by aaron-bruce

Before generating the url just call setHostedDomain against your Google Client

$client->setHostedDomain("HOSTED DOMAIN")

For login with Google using Laravel Socialite https://laravel.com/docs/8.x/socialite#optional-parameters

use Laravel\Socialite\Facades\Socialite;


return Socialite::driver('google')
->with(['hd' => 'pontomais.com.br'])
->redirect();