From dfac8fb53816203f50f0ec2dbe3af43f9479667f Mon Sep 17 00:00:00 2001
From: Eric Tokatlian
Date: Mon, 1 Jul 2019 20:41:24 -0700
Subject: [PATCH] Adds google auth
---
app/app.js | 1 +
app/models/googleoauthconfig.js | 5 +
app/styles/pages/_settings.scss | 4 +
app/verify-auth/route.js | 24 +-
lib/global-admin/addon/engine.js | 1 +
.../addon/mixins/authentication.js | 2 +-
lib/global-admin/addon/routes.js | 1 +
.../security/authentication/controller.js | 7 +
.../authentication/github/template.hbs | 2 +-
.../authentication/googleoauth/controller.js | 75 +++++++
.../authentication/googleoauth/route.js | 31 +++
.../authentication/googleoauth/template.hbs | 210 ++++++++++++++++++
.../authentication/googleoauth/controller.js | 1 +
.../authentication/googleoauth/route.js | 1 +
.../components/login-google/component.js | 12 +
.../components/login-google/template.hbs | 5 +
lib/login/addon/login/controller.js | 4 +
lib/login/addon/login/template.hbs | 4 +
lib/shared/addon/google/service.js | 148 ++++++++++++
lib/shared/addon/pipeline-github/service.js | 2 +-
lib/shared/app/google/service.js | 1 +
.../images/providers/provider-google.svg | 9 +
translations/en-us.yaml | 65 ++++++
23 files changed, 607 insertions(+), 8 deletions(-)
create mode 100644 app/models/googleoauthconfig.js
create mode 100644 lib/global-admin/addon/security/authentication/googleoauth/controller.js
create mode 100644 lib/global-admin/addon/security/authentication/googleoauth/route.js
create mode 100644 lib/global-admin/addon/security/authentication/googleoauth/template.hbs
create mode 100644 lib/global-admin/app/security/authentication/googleoauth/controller.js
create mode 100644 lib/global-admin/app/security/authentication/googleoauth/route.js
create mode 100644 lib/login/addon/components/login-google/component.js
create mode 100644 lib/login/addon/components/login-google/template.hbs
create mode 100644 lib/shared/addon/google/service.js
create mode 100644 lib/shared/app/google/service.js
create mode 100644 public/assets/images/providers/provider-google.svg
diff --git a/app/app.js b/app/app.js
index 655be43b0..e54e9a501 100644
--- a/app/app.js
+++ b/app/app.js
@@ -79,6 +79,7 @@ const App = Application.extend({
'digitalOcean',
'endpoint',
'github',
+ 'google',
'globalStore',
'intl',
'modal',
diff --git a/app/models/googleoauthconfig.js b/app/models/googleoauthconfig.js
new file mode 100644
index 000000000..70a1fa163
--- /dev/null
+++ b/app/models/googleoauthconfig.js
@@ -0,0 +1,5 @@
+import Resource from '@rancher/ember-api-store/models/resource';
+
+var GoogleOauthConfig = Resource.extend({ type: 'googleOauthConfig', });
+
+export default GoogleOauthConfig;
diff --git a/app/styles/pages/_settings.scss b/app/styles/pages/_settings.scss
index 5d4b19c58..f86b09900 100644
--- a/app/styles/pages/_settings.scss
+++ b/app/styles/pages/_settings.scss
@@ -67,6 +67,10 @@
&.freeipa {
background-image: url('images/providers/provider-freeipa.svg');
}
+
+ &.googleoauth {
+ background-image: url('images/providers/provider-google.svg');
+ }
}
.advanced-setting {
diff --git a/app/verify-auth/route.js b/app/verify-auth/route.js
index b4d0fbf57..800ebedb7 100644
--- a/app/verify-auth/route.js
+++ b/app/verify-auth/route.js
@@ -12,6 +12,7 @@ const allowedForwards = ['localhost'];
export default Route.extend(VerifyAuth, {
github: service(),
+ google: service(),
intl: service(),
language: service('user-language'),
@@ -23,6 +24,7 @@ export default Route.extend(VerifyAuth, {
model(params/* , transition */) {
const github = get(this, 'github');
+ const google = get(this, 'google');
const code = get(params, 'code');
const forward = get(params, 'forward');
@@ -50,11 +52,13 @@ export default Route.extend(VerifyAuth, {
if ( window.opener && !get(params, 'login') && !get(params, 'errorCode') ) {
let openersGithub = window.opener.ls('github');
+ let openersGoogle = window.opener.ls('google');
let openerStore = window.opener.ls('globalStore');
let qp = get(params, 'config') || get(params, 'authProvider');
let type = `${ qp }Config`;
let config = openerStore.getById(type, qp);
let gh = get(this, 'github');
+ let go = get(this, 'google');
let stateMsg = 'Authorization state did not match, please try again.';
if ( get(params, 'config') === 'github' ) {
@@ -63,6 +67,12 @@ export default Route.extend(VerifyAuth, {
}).catch((err) => {
this.send('gotError', err);
});
+ } else if ( get(params, 'config') === 'googleoauth') {
+ return go.testConfig(config).then((resp) => {
+ go.authorize(resp, openersGoogle.get('state'));
+ }).catch((err) => {
+ this.send('gotError', err)
+ })
} else if ( samlProviders.includes(get(params, 'config')) ) {
if ( window.opener.window.onAuthTest ) {
reply(null, config);
@@ -72,7 +82,9 @@ export default Route.extend(VerifyAuth, {
}
if ( get(params, 'code') ) {
- if ( openersGithub.stateMatches(get(params, 'state')) ) {
+ const currentOpener = openersGithub.state ? openersGithub : openersGoogle;
+
+ if ( currentOpener.stateMatches(get(params, 'state')) ) {
reply(params.error_description, params.code);
} else {
reply(stateMsg);
@@ -87,11 +99,13 @@ export default Route.extend(VerifyAuth, {
}
}
- if ( code && get(params, 'login') ) {
- if ( github.stateMatches(get(params, 'state')) ) {
- let ghProvider = get(this, 'access.providers').findBy('id', 'github');
+ if ( code && get(params, 'login') || get(params, 'state').includes('login') ) {
+ let currentProvider = github.stateMatches(get(params, 'state')) ? 'github' : 'googleoauth'
- return ghProvider.doAction('login', {
+ if ( github.stateMatches(get(params, 'state')) || google.stateMatches(get(params, 'state')) ) {
+ currentProvider = get(this, 'access.providers').findBy('id', currentProvider);
+
+ return currentProvider.doAction('login', {
code,
responseType: 'cookie',
description: C.SESSION.DESCRIPTION,
diff --git a/lib/global-admin/addon/engine.js b/lib/global-admin/addon/engine.js
index 8cfca7763..a7ac984d8 100644
--- a/lib/global-admin/addon/engine.js
+++ b/lib/global-admin/addon/engine.js
@@ -18,6 +18,7 @@ const Eng = Engine.extend({
'digitalOcean',
'endpoint',
'github',
+ 'google',
'globalStore',
'intl',
'modal',
diff --git a/lib/global-admin/addon/mixins/authentication.js b/lib/global-admin/addon/mixins/authentication.js
index f6892c682..961b78a96 100644
--- a/lib/global-admin/addon/mixins/authentication.js
+++ b/lib/global-admin/addon/mixins/authentication.js
@@ -61,7 +61,7 @@ export default Mixin.create({
this.send('clearError');
const model = get(this, 'authConfig');
- const accessMode = get(model, 'id') === 'github' ? 'restricted' : 'unrestricted';
+ const accessMode = get(model, 'id') === 'github' || 'googleoauth' ? 'restricted' : 'unrestricted';
setProperties(model, {
enabled: false,
diff --git a/lib/global-admin/addon/routes.js b/lib/global-admin/addon/routes.js
index a12574837..6bf0e9dd6 100644
--- a/lib/global-admin/addon/routes.js
+++ b/lib/global-admin/addon/routes.js
@@ -69,6 +69,7 @@ export default buildRoutes(function() {
this.route('adfs');
this.route('okta');
this.route('freeipa');
+ this.route('googleoauth');
});
this.route('cloud-credentials', function() {
diff --git a/lib/global-admin/addon/security/authentication/controller.js b/lib/global-admin/addon/security/authentication/controller.js
index 4752bbf12..8086cf9a7 100644
--- a/lib/global-admin/addon/security/authentication/controller.js
+++ b/lib/global-admin/addon/security/authentication/controller.js
@@ -73,6 +73,13 @@ export default Controller.extend({
available: this.hasRecord('openldapconfig'),
providerType: 'ldap',
},
+ {
+ route: 'security.authentication.googleoauth',
+ label: 'Google',
+ css: 'googleoauth',
+ available: this.hasRecord('googleoauthconfig'),
+ providerType: null,
+ },
// {route: 'security.authentication.shibboleth', label: 'Shibboleth', css: 'shibboleth', available: this.hasRecord('shibbolethconfig') },
];
}),
diff --git a/lib/global-admin/addon/security/authentication/github/template.hbs b/lib/global-admin/addon/security/authentication/github/template.hbs
index e5f870d1f..19833e93c 100644
--- a/lib/global-admin/addon/security/authentication/github/template.hbs
+++ b/lib/global-admin/addon/security/authentication/github/template.hbs
@@ -147,4 +147,4 @@
{{/accordion-list-item}}
{{/unless}}
-{{/accordion-list}}
+{{/accordion-list}}
\ No newline at end of file
diff --git a/lib/global-admin/addon/security/authentication/googleoauth/controller.js b/lib/global-admin/addon/security/authentication/googleoauth/controller.js
new file mode 100644
index 000000000..7173a9bfa
--- /dev/null
+++ b/lib/global-admin/addon/security/authentication/googleoauth/controller.js
@@ -0,0 +1,75 @@
+import { get, set, computed, setProperties } from '@ember/object';
+import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+import Controller from '@ember/controller';
+import AuthMixin from 'global-admin/mixins/authentication';
+
+export default Controller.extend(AuthMixin, {
+ google: service(),
+ endpoint: service(),
+ access: service(),
+ settings: service(),
+
+ confirmDisable: false,
+ errors: null,
+ testing: false,
+ error: null,
+ saved: false,
+ saving: false,
+ haveToken: false,
+
+ organizations: null,
+ secure: true,
+
+ authConfig: alias('model.googleConfig'),
+ isEnabled: alias('authConfig.enabled'),
+
+ actions: {
+ save() {
+ this.send('clearError');
+ set(this, 'saving', true);
+
+ const authConfig = get(this, 'authConfig');
+ const am = 'unrestricted';
+
+ setProperties(authConfig, {
+ 'oauthCredential': (authConfig.get('oauthCredential') || '').trim(),
+ 'serviceAccountCredential': (authConfig.get('serviceAccountCredential') || '').trim(),
+ 'adminEmail': (authConfig.get('adminEmail') || '').trim(),
+ 'hostname': (authConfig.get('hostname') || '').trim(),
+ 'enabled': false,
+ 'accessMode': am,
+ 'tls': true,
+ 'allowedPrincipalIds': [],
+ });
+
+ set(this, '_boundSucceed', this.authenticationApplied.bind(this));
+ get(this, 'google').test(authConfig, get(this, '_boundSucceed'));
+ },
+ },
+
+ destinationUrl: computed(() => {
+ return `${ window.location.origin }/`;
+ }),
+
+ destinationDomain: computed(() => {
+ return `${ window.location.hostname }`
+ }),
+
+ redirectURI: computed(() => {
+ return `${ window.location.origin }/verify-auth`
+ }),
+
+ authenticationApplied(err) {
+ set(this, 'saving', false);
+
+ if (err) {
+ set(this, 'isEnabled', false);
+ this.send('gotError', err);
+
+ return;
+ }
+
+ this.send('clearError');
+ },
+});
diff --git a/lib/global-admin/addon/security/authentication/googleoauth/route.js b/lib/global-admin/addon/security/authentication/googleoauth/route.js
new file mode 100644
index 000000000..2e0adb848
--- /dev/null
+++ b/lib/global-admin/addon/security/authentication/googleoauth/route.js
@@ -0,0 +1,31 @@
+import Route from '@ember/routing/route';
+import { inject as service } from '@ember/service';
+import { get } from '@ember/object';
+import { hash } from 'rsvp';
+
+export default Route.extend({
+ globalStore: service(),
+
+ model() {
+ let gs = get(this, 'globalStore');
+
+ return hash({
+ googleConfig: gs.find('authconfig', 'googleoauth', { forceReload: true }),
+ principals: gs.all('principal')
+ }).catch((e) => {
+ return e;
+ })
+ },
+
+ setupController(controller, model) {
+ controller.setProperties({
+ model,
+ confirmDisable: false,
+ testing: false,
+ organizations: get(this, 'session.orgs') || [],
+ errors: null,
+ });
+
+ controller.set('saved', true);
+ }
+});
diff --git a/lib/global-admin/addon/security/authentication/googleoauth/template.hbs b/lib/global-admin/addon/security/authentication/googleoauth/template.hbs
new file mode 100644
index 000000000..4559c8e82
--- /dev/null
+++ b/lib/global-admin/addon/security/authentication/googleoauth/template.hbs
@@ -0,0 +1,210 @@
+
+ {{#unless isEnabled}}
+
+
+
+
{{t 'authPage.google.header.disabled.label'}}
+
+
+ {{/unless}}
+ {{top-errors errors=errors}}
+
+
+{{#accordion-list showExpandAll=false as |al expandFn|}}
+
+ {{#if isEnabled}}
+ {{#accordion-list-item
+ expand=(action expandFn)
+ expandAll=al.expandAll
+ expandOnInit=true
+ expanded=true
+ showExpand=false
+ title=(t 'authPage.google.authenticated.header.text')
+ }}
+
+
+
+
+
+
+
+ {{t 'authPage.google.authenticated.header.adminEmail.text'}} {{authConfig.adminEmail}}
+
+ {{/accordion-list-item}}
+
+ {{#accordion-list-item
+ classNames="mt-30"
+ detail=(t 'siteAccess.helpText' appName=settings.appName htmlSafe=true)
+ expand=(action expandFn)
+ expandAll=al.expandAll
+ expandOnInit=true
+ expanded=true
+ showExpand=false
+ title=(t 'siteAccess.header')
+ }}
+ {{site-access
+ model=authConfig
+ principals=model.principals
+ collection='siteAccess.organizations'
+ }}
+ {{/accordion-list-item}}
+ {{/if}}
+
+ {{#unless isEnabled}}
+ {{#accordion-list-item
+ expand=(action expandFn)
+ expandAll=al.expandAll
+ expandOnInit=true
+ expanded=true
+ showExpand=false
+ title=(t 'authPage.google.notAuthenticated.header')
+ }}
+
+
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li1.text' htmlSafe=true}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li1.ul.li1'}}
+
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li2.text'}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li2.ul.li1' appName=settings.appName htmlSafe=true}}
+ {{destinationDomain}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li2.ul.li2' htmlSafe=true}} {{destinationUrl}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li2.ul.li3'}}
+
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li3.text'}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li3.ul.li1'}}
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li3.ul.li2' htmlSafe=true}} {{destinationUrl}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li3.ul.li3' htmlSafe=true}}
+ {{redirectURI}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li3.ul.li4'}}
+
+
+ -
+ {{t 'authPage.google.notAuthenticated.ul.li4.text'}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li4.ul.li1'}}
+
+ - {{t 'authPage.google.notAuthenticated.ul.li4.ul.li2'}}
+ - {{t 'authPage.google.notAuthenticated.ul.li4.ul.li3'}}
+
+
+
+
+
+ {{/accordion-list-item}}
+
+ {{#accordion-list-item
+ expand=(action expandFn)
+ expandAll=al.expandAll
+ expandOnInit=true
+ expanded=true
+ showExpand=false
+ title=(t 'authPage.google.notAuthenticated.form.header' appName=settings.appName)
+ }}
+
+ {{/accordion-list-item}}
+ {{/unless}}
+{{/accordion-list}}
\ No newline at end of file
diff --git a/lib/global-admin/app/security/authentication/googleoauth/controller.js b/lib/global-admin/app/security/authentication/googleoauth/controller.js
new file mode 100644
index 000000000..f31618756
--- /dev/null
+++ b/lib/global-admin/app/security/authentication/googleoauth/controller.js
@@ -0,0 +1 @@
+export { default } from 'global-admin/security/authentication/googleoauth/controller';
diff --git a/lib/global-admin/app/security/authentication/googleoauth/route.js b/lib/global-admin/app/security/authentication/googleoauth/route.js
new file mode 100644
index 000000000..afdf6757d
--- /dev/null
+++ b/lib/global-admin/app/security/authentication/googleoauth/route.js
@@ -0,0 +1 @@
+export { default } from './global-admin/security/authentication/googleoauth/route';
diff --git a/lib/login/addon/components/login-google/component.js b/lib/login/addon/components/login-google/component.js
new file mode 100644
index 000000000..a4d197dc5
--- /dev/null
+++ b/lib/login/addon/components/login-google/component.js
@@ -0,0 +1,12 @@
+import { inject as service } from '@ember/service';
+import Component from '@ember/component';
+
+export default Component.extend({
+ google: service(),
+
+ actions: {
+ authenticate() {
+ this.get('google').login();
+ }
+ }
+});
diff --git a/lib/login/addon/components/login-google/template.hbs b/lib/login/addon/components/login-google/template.hbs
new file mode 100644
index 000000000..e8e118063
--- /dev/null
+++ b/lib/login/addon/components/login-google/template.hbs
@@ -0,0 +1,5 @@
+
diff --git a/lib/login/addon/login/controller.js b/lib/login/addon/login/controller.js
index c2c4188e1..f9ee97cef 100644
--- a/lib/login/addon/login/controller.js
+++ b/lib/login/addon/login/controller.js
@@ -150,6 +150,10 @@ export default Controller.extend({
return !!get(this, 'access.providers').findBy('id', 'github');
}),
+ isGoogle: computed('access.providers', function() {
+ return !!get(this, 'access.providers').findBy('id', 'googleoauth');
+ }),
+
isPing: computed('access.providers', function() {
return !!get(this, 'access.providers').findBy('id', 'ping');
}),
diff --git a/lib/login/addon/login/template.hbs b/lib/login/addon/login/template.hbs
index 8e0e8cce9..23b321e59 100644
--- a/lib/login/addon/login/template.hbs
+++ b/lib/login/addon/login/template.hbs
@@ -31,6 +31,10 @@
{{login-github action=(action "started")}}
{{/if}}
+ {{#if isGoogle}}
+ {{login-google action=(action "started")}}
+ {{/if}}
+
{{#if isShibboleth}}
{{t "loginPage.shibbolethMessage" appName=settings.appName}}
diff --git a/lib/shared/addon/google/service.js b/lib/shared/addon/google/service.js
new file mode 100644
index 000000000..cd30c1c4a
--- /dev/null
+++ b/lib/shared/addon/google/service.js
@@ -0,0 +1,148 @@
+import Service, { inject as service } from '@ember/service';
+import { addQueryParam, addQueryParams, popupWindowOptions } from 'shared/utils/util';
+import { get, set } from '@ember/object';
+import C from 'shared/utils/constants';
+
+const googleOauthScope = 'openid profile email https://www.googleapis.com/auth/admin.directory.user.readonly https://www.googleapis.com/auth/admin.directory.group.readonly'
+
+export default Service.extend({
+ access: service(),
+ cookies: service(),
+ session: service(),
+ globalStore: service(),
+ app: service(),
+ intl: service(),
+
+ generateState() {
+ return set(this, 'session.googleState', `${ Math.random() }`);
+ },
+
+ generateLoginStateKey() {
+ return set(this, 'session.googleState', `${ Math.random() }login`)
+ },
+
+ stateMatches(actual) {
+ return actual && get(this, 'session.googleState') === actual;
+ },
+
+ testConfig(config) {
+ return config.doAction('configureTest', config);
+ },
+
+ saveConfig(config, opt) {
+ return config.doAction('testAndApply', opt);
+ },
+
+ authorize(auth, state) {
+ const url = addQueryParams(get(auth, 'redirectUrl'), {
+ scope: googleOauthScope,
+ redirect_uri: `${ window.location.origin }/verify-auth`,
+ state,
+ });
+
+
+ return window.location.href = url;
+ },
+
+ login(forwardUrl) {
+ const provider = get(this, 'access.providers').findBy('id', 'googleoauth');
+ const authRedirect = get(provider, 'redirectUrl');
+ let redirect = `${ window.location.origin }/verify-auth`;
+
+ if ( forwardUrl ) {
+ redirect = addQueryParam(redirect, 'forward', forwardUrl);
+ }
+
+ const url = addQueryParams(authRedirect, {
+ scope: googleOauthScope,
+ state: this.generateLoginStateKey(),
+ redirect_uri: redirect,
+ });
+
+ window.location.href = url;
+ },
+
+ test(config, cb) {
+ let responded = false;
+
+ window.onAuthTest = (err, code) => {
+ if ( !responded ) {
+ let googleConfig = config;
+
+ responded = true;
+
+ this.finishTest(googleConfig, code, cb);
+ }
+ };
+
+ set(this, 'state', this.generateState());
+
+ let url = addQueryParams(`${ window.location.origin }/verify-auth`, { config: 'googleoauth', });
+
+ const popup = window.open(url, 'rancherAuth', popupWindowOptions());
+ const intl = get(this, 'intl');
+
+ let timer = setInterval(() => {
+ if (popup && popup.closed ) {
+ clearInterval(timer);
+
+ if ( !responded ) {
+ responded = true;
+ cb({
+ type: 'error',
+ message: intl.t('authPage.google.testAuth.authError')
+ });
+ }
+ } else if (popup === null || typeof (popup) === 'undefined') {
+ clearInterval(timer);
+
+ if ( !responded ) {
+ responded = true;
+
+ cb({
+ type: 'error',
+ message: intl.t('authPage.google.testAuth.popupError')
+ });
+ }
+ }
+ }, 500);
+ },
+
+ finishTest(config, code, cb) {
+ const goConfig = config;
+
+ set(goConfig, 'enabled', true);
+
+ let out = {
+ code,
+ enabled: true,
+ googleOauthConfig: goConfig,
+ description: C.SESSION.DESCRIPTION,
+ ttl: C.SESSION.TTL,
+ };
+
+ const allowedPrincipalIds = get(config, 'allowedPrincipalIds') || [];
+
+ return this.saveConfig(config, out).then(() => {
+ let found = false;
+ const myPIds = get(this, 'access.me.principalIds');
+
+ myPIds.forEach( (id) => {
+ if (allowedPrincipalIds.indexOf(id) >= 0) {
+ found = true;
+ }
+ });
+
+ if ( !found && !allowedPrincipalIds.length) {
+ allowedPrincipalIds.pushObject(get(this, 'access.principal.id'));
+ }
+
+ return goConfig.save().then(() => {
+ window.location.href = window.location.href;
+ });
+ })
+ .catch((err) => {
+ cb(err);
+ });
+ },
+})
diff --git a/lib/shared/addon/pipeline-github/service.js b/lib/shared/addon/pipeline-github/service.js
index 4a64e7984..ee916c38f 100644
--- a/lib/shared/addon/pipeline-github/service.js
+++ b/lib/shared/addon/pipeline-github/service.js
@@ -65,4 +65,4 @@ export default Service.extend({
}
}, 500);
},
-});
\ No newline at end of file
+});
diff --git a/lib/shared/app/google/service.js b/lib/shared/app/google/service.js
new file mode 100644
index 000000000..e8bf42af0
--- /dev/null
+++ b/lib/shared/app/google/service.js
@@ -0,0 +1 @@
+export { default } from 'shared/google/service';
diff --git a/public/assets/images/providers/provider-google.svg b/public/assets/images/providers/provider-google.svg
new file mode 100644
index 000000000..90064d7de
--- /dev/null
+++ b/public/assets/images/providers/provider-google.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/translations/en-us.yaml b/translations/en-us.yaml
index 2cd115699..a01073e7c 100644
--- a/translations/en-us.yaml
+++ b/translations/en-us.yaml
@@ -520,6 +520,68 @@ authPage:
post: Waiting to hear back from GitHub
authError: 'Github access was not authorized'
popupError: 'Please disable your pop-up blocker and click "Authenticate" again.'
+ google:
+ header:
+ disabled:
+ label: 'Google is not configured'
+ authenticated:
+ header:
+ text: Authentication
+ adminEmail:
+ text: "Admin Email: "
+ disableAccess:
+ header: "Danger Zone™"
+ warning: 'Caution: Disabling access control will give complete control over {appName} to anyone that can reach this page or the API.'
+ confirmDisable: "Are you sure? Click again to disable access control"
+ disable: Disable Google access
+ notAuthenticated:
+ header: "1. Configure your Google Application settings"
+ ul:
+ li1:
+ text: 'For standard Google, click here to go to your Google developer console.'
+ ul:
+ li1: 'Login to your account. Navigate to "APIs & Services" and then select "Credentials".'
+ li2:
+ text: 'Navigate to the "OAuth consent screen" tab and fill in the form:'
+ ul:
+ li1: 'Authorized domains: '
+ li2: 'Application homepage link: '
+ li3: 'Enable "email", "profile", and "openid" to Scopes for Google APIs.'
+ li4: 'Authorization callback URL:'
+ li3:
+ text: 'Navigate to the "Credentials" tab to create your OAuth client ID:'
+ ul:
+ li1: 'Select "Create Credentials", select OAuth clientID, then select Web application.'
+ li2: 'Authorized Javascript origins: '
+ li3: 'Authorized redirect URIs: '
+ li4: 'Click "save" and then download JSON.'
+ li4:
+ text: 'Navigate to the "Credentials" tab again to create your Service account key:'
+ ul:
+ li1: 'Select your service account.'
+ li2: 'Select JSON for your Key type and then click "Create".'
+ li3: 'A JSON file will be automatically saved locally.'
+ form:
+ header: '2. Configure {appName} to use your application for authentication'
+ oauthCredential:
+ labelText: OAuth Credentials
+ helperText: Copy and paste in the OAuth Credentials JSON which can be found in your Google API developers console (Step 3 above).
+ serviceAccountCredential:
+ labelText: Service Account Credentials
+ helperText: Copy and paste in the Service Account Credentials JSON which can be found in the service accounts section of the Google API developers console (Step 4 above).
+ adminEmail:
+ labelText: Admin Email
+ helperText: Enter the Admin Email associated with your account.
+ hostname:
+ labelText: Host name
+ helperText: Enter the host name of your account.
+ testAuth:
+ buttonText:
+ pre: Authenticate with Google
+ post: Waiting to hear back from Google
+ authError: 'Google access was not authorized'
+ popupError: 'Please disable your pop-up blocker and click "Authenticate" again.'
+
azuread:
header:
disabled:
@@ -5622,6 +5684,9 @@ loginShibboleth:
loginGithub:
buttonText: Log In with GitHub
+loginGoogle:
+ buttonText: Log In with Google
+
loginAzure:
buttonText: Log In with Azure AD