feat: auth self-registration config + gql grouping
This commit is contained in:
parent
49834461a6
commit
0afa65fa58
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
||||||
v-card.pa-3(flat, tile)
|
v-card.pa-3(flat, tile)
|
||||||
v-subheader.pl-0.pb-2 Select which authentication strategies to enable:
|
.body-2.grey--text.text--darken-1 Select which authentication strategies to enable:
|
||||||
|
.caption.grey--text.pb-2 Some strategies require additional configuration in their dedicated tab (when selected).
|
||||||
v-form
|
v-form
|
||||||
v-checkbox(
|
v-checkbox(
|
||||||
v-for='strategy in strategies',
|
v-for='strategy in strategies',
|
||||||
@ -27,19 +28,36 @@
|
|||||||
v-form
|
v-form
|
||||||
v-subheader.pl-0 Strategy Configuration
|
v-subheader.pl-0 Strategy Configuration
|
||||||
.body-1.ml-3(v-if='!strategy.config || strategy.config.length < 1') This strategy has no configuration options you can modify.
|
.body-1.ml-3(v-if='!strategy.config || strategy.config.length < 1') This strategy has no configuration options you can modify.
|
||||||
v-text-field(v-else, v-for='cfg in strategy.config', :key='cfg.key', :label='cfg.key', prepend-icon='settings_applications')
|
v-text-field(
|
||||||
|
v-else
|
||||||
|
v-for='cfg in strategy.config'
|
||||||
|
:key='cfg.key'
|
||||||
|
:label='cfg.key'
|
||||||
|
v-model='cfg.value'
|
||||||
|
prepend-icon='settings_applications'
|
||||||
|
)
|
||||||
v-divider
|
v-divider
|
||||||
v-subheader.pl-0 Registration
|
v-subheader.pl-0 Registration
|
||||||
v-switch.ml-3(
|
v-switch.ml-3(
|
||||||
v-model='auths',
|
v-model='allowSelfRegistration',
|
||||||
label='Allow self-registration',
|
label='Allow self-registration',
|
||||||
:value='true',
|
:value='true',
|
||||||
color='primary',
|
color='primary',
|
||||||
hint='Allow any user successfully authorized by the strategy to access the wiki.',
|
hint='Allow any user successfully authorized by the strategy to access the wiki.',
|
||||||
persistent-hint
|
persistent-hint
|
||||||
)
|
)
|
||||||
v-text-field.ml-3(label='Limit to specific email domains', prepend-icon='mail_outline')
|
v-text-field.ml-3(
|
||||||
v-text-field.ml-3(label='Assign to group', prepend-icon='people')
|
label='Limit to specific email domains'
|
||||||
|
prepend-icon='mail_outline'
|
||||||
|
hint='Domain(s) seperated by comma. (e.g. domain1.com, domain2.com)'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
v-text-field.ml-3(
|
||||||
|
label='Assign to group'
|
||||||
|
prepend-icon='people'
|
||||||
|
hint='Automatically assign new users to these groups.'
|
||||||
|
persistent-hint
|
||||||
|
)
|
||||||
|
|
||||||
v-card-chin
|
v-card-chin
|
||||||
v-btn(color='primary')
|
v-btn(color='primary')
|
||||||
@ -54,14 +72,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import strategiesQuery from 'gql/admin-auth-query-strategies.gql'
|
import strategiesQuery from 'gql/admin/auth/auth-query-strategies.gql'
|
||||||
import strategiesSaveMutation from 'gql/admin-auth-mutation-save-strategies.gql'
|
import strategiesSaveMutation from 'gql/admin/auth/auth-mutation-save-strategies.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
strategies: [],
|
strategies: [],
|
||||||
selectedStrategies: ['local']
|
selectedStrategies: ['local'],
|
||||||
|
selfRegistration: false,
|
||||||
|
domainWhitelist: [],
|
||||||
|
autoEnrollGroups: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -114,11 +114,11 @@
|
|||||||
import Criterias from '../common/criterias.vue'
|
import Criterias from '../common/criterias.vue'
|
||||||
import UserSearch from '../common/user-search.vue'
|
import UserSearch from '../common/user-search.vue'
|
||||||
|
|
||||||
import groupQuery from 'gql/admin-groups-query-single.gql'
|
import groupQuery from 'gql/admin/groups/groups-query-single.gql'
|
||||||
import assignUserMutation from 'gql/admin-groups-mutation-assign.gql'
|
import assignUserMutation from 'gql/admin/groups/groups-mutation-assign.gql'
|
||||||
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
|
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
|
||||||
import unassignUserMutation from 'gql/admin-groups-mutation-unassign.gql'
|
import unassignUserMutation from 'gql/admin/groups/groups-mutation-unassign.gql'
|
||||||
import updateGroupMutation from 'gql/admin-groups-mutation-update.gql'
|
import updateGroupMutation from 'gql/admin/groups/groups-mutation-update.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
.dialog-header.is-short New Group
|
.dialog-header.is-short New Group
|
||||||
v-card-text
|
v-card-text
|
||||||
v-text-field(v-model='newGroupName', label='Group Name', autofocus, counter='255', @keyup.enter='createGroup')
|
v-text-field(v-model='newGroupName', label='Group Name', autofocus, counter='255', @keyup.enter='createGroup')
|
||||||
v-card-actions
|
v-card-chin
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(flat, @click='newGroupDialog = false') Cancel
|
v-btn(flat, @click='newGroupDialog = false') Cancel
|
||||||
v-btn(color='primary', @click='createGroup') Create
|
v-btn(color='primary', @click='createGroup') Create
|
||||||
@ -45,9 +45,9 @@
|
|||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import groupsQuery from 'gql/admin-groups-query-list.gql'
|
import groupsQuery from 'gql/admin/groups/groups-query-list.gql'
|
||||||
import createGroupMutation from 'gql/admin-groups-mutation-create.gql'
|
import createGroupMutation from 'gql/admin/groups/groups-mutation-create.gql'
|
||||||
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
|
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -128,9 +128,9 @@ import _ from 'lodash'
|
|||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
import localesQuery from 'gql/admin-locale-query-list.gql'
|
import localesQuery from 'gql/admin/locale/locale-query-list.gql'
|
||||||
import localesDownloadMutation from 'gql/admin-locale-mutation-download.gql'
|
import localesDownloadMutation from 'gql/admin/locale/locale-mutation-download.gql'
|
||||||
import localesSaveMutation from 'gql/admin-locale-mutation-save.gql'
|
import localesSaveMutation from 'gql/admin/locale/locale-mutation-save.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -106,7 +106,7 @@ import IconCube from 'mdi/cube'
|
|||||||
import IconDatabase from 'mdi/database'
|
import IconDatabase from 'mdi/database'
|
||||||
import IconNodeJs from 'mdi/nodejs'
|
import IconNodeJs from 'mdi/nodejs'
|
||||||
|
|
||||||
import systemInfoQuery from 'gql/admin-system-query-info.gql'
|
import systemInfoQuery from 'gql/admin/system/system-query-info.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import themeSaveMutation from 'gql/admin-theme-mutation-save.gql'
|
import themeSaveMutation from 'gql/admin/theme/theme-mutation-save.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import searchUsersQuery from 'gql/common-users-query-search.gql'
|
import searchUsersQuery from 'gql/common/common-users-query-search.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
filters: {
|
filters: {
|
||||||
|
@ -56,9 +56,9 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
import strategiesQuery from 'gql/login-query-strategies.gql'
|
import strategiesQuery from 'gql/login/login-query-strategies.gql'
|
||||||
import loginMutation from 'gql/login-mutation-login.gql'
|
import loginMutation from 'gql/login/login-mutation-login.gql'
|
||||||
import tfaMutation from 'gql/login-mutation-tfa.gql'
|
import tfaMutation from 'gql/login/login-mutation-tfa.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18nOptions: { namespaces: 'auth' },
|
i18nOptions: { namespaces: 'auth' },
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
v-app.profile
|
v-app(:dark='darkMode').profile
|
||||||
nav-header
|
nav-header
|
||||||
v-navigation-drawer.pb-0(v-model='profileDrawerShown', app, fixed, clipped, left, permanent)
|
v-navigation-drawer.pb-0(v-model='profileDrawerShown', app, fixed, clipped, left, permanent)
|
||||||
v-list(dense)
|
v-list(dense)
|
||||||
@ -22,7 +22,7 @@
|
|||||||
transition(name='profile-router')
|
transition(name='profile-router')
|
||||||
router-view
|
router-view
|
||||||
|
|
||||||
v-footer.py-2.justify-center(app, absolute, color='grey lighten-3', inset, height='auto')
|
v-footer.py-2.justify-center(app, absolute, :color='darkMode ? "" : "grey lighten-3"', inset, height='auto')
|
||||||
.caption.grey--text.text--darken-1 Powered by Wiki.js
|
.caption.grey--text.text--darken-1 Powered by Wiki.js
|
||||||
|
|
||||||
v-snackbar(
|
v-snackbar(
|
||||||
@ -41,7 +41,7 @@
|
|||||||
import VueRouter from 'vue-router'
|
import VueRouter from 'vue-router'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI, siteConfig */
|
||||||
|
|
||||||
const router = new VueRouter({
|
const router = new VueRouter({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
@ -75,7 +75,8 @@ export default {
|
|||||||
notificationState: {
|
notificationState: {
|
||||||
get() { return this.notification.isActive },
|
get() { return this.notification.isActive },
|
||||||
set(newState) { this.$store.commit('updateNotificationState', newState) }
|
set(newState) { this.$store.commit('updateNotificationState', newState) }
|
||||||
}
|
},
|
||||||
|
darkMode() { return siteConfig.darkMode }
|
||||||
},
|
},
|
||||||
router
|
router
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@
|
|||||||
v-divider
|
v-divider
|
||||||
v-subheader.pl-0 Timezone
|
v-subheader.pl-0 Timezone
|
||||||
v-select.grey.lighten-5(solo, flat)
|
v-select.grey.lighten-5(solo, flat)
|
||||||
v-divider.my-0
|
v-card-chin
|
||||||
v-card-actions.grey.lighten-4
|
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(color='primary')
|
v-btn(color='primary')
|
||||||
v-icon(left) chevron_right
|
v-icon(left) chevron_right
|
||||||
@ -32,8 +31,7 @@
|
|||||||
v-card-text
|
v-card-text
|
||||||
v-subheader.pl-0 Default Editor
|
v-subheader.pl-0 Default Editor
|
||||||
v-select.grey.lighten-5(solo, flat)
|
v-select.grey.lighten-5(solo, flat)
|
||||||
v-divider.my-0
|
v-card-chin
|
||||||
v-card-actions.grey.lighten-4
|
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(color='primary')
|
v-btn(color='primary')
|
||||||
v-icon(left) chevron_right
|
v-icon(left) chevron_right
|
||||||
@ -42,10 +40,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/* global siteConfig */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return { }
|
return { }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
darkMode() { return siteConfig.darkMode }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
v-text-field(label='Name', :counter='255', v-model='name', prepend-icon='person')
|
v-text-field(label='Name', :counter='255', v-model='name', prepend-icon='person')
|
||||||
v-text-field(label='Job Title', :counter='255', prepend-icon='accessibility')
|
v-text-field(label='Job Title', :counter='255', prepend-icon='accessibility')
|
||||||
v-text-field(label='Location / Office', :counter='255', prepend-icon='location_on')
|
v-text-field(label='Location / Office', :counter='255', prepend-icon='location_on')
|
||||||
v-divider.my-0
|
v-card-chin
|
||||||
v-card-actions.grey.lighten-4
|
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(color='primary')
|
v-btn(color='primary')
|
||||||
v-icon(left) chevron_right
|
v-icon(left) chevron_right
|
||||||
@ -29,8 +28,13 @@
|
|||||||
.subheading Authentication
|
.subheading Authentication
|
||||||
v-card-text
|
v-card-text
|
||||||
v-subheader.pl-0 Provider
|
v-subheader.pl-0 Provider
|
||||||
v-toolbar(flat, color='purple lighten-5', dense).purple--text.text--darken-4
|
v-toolbar(
|
||||||
v-icon(color='purple darken-4') supervised_user_circle
|
flat
|
||||||
|
:color='darkMode ? "grey darken-2" : "purple lighten-5"'
|
||||||
|
dense
|
||||||
|
:class='darkMode ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
|
||||||
|
)
|
||||||
|
v-icon(:color='darkMode ? "grey lighten-1" : "purple darken-4"') supervised_user_circle
|
||||||
.subheading.ml-3 Local
|
.subheading.ml-3 Local
|
||||||
v-divider
|
v-divider
|
||||||
v-subheader.pl-0 Two-Factor Authentication (2FA)
|
v-subheader.pl-0 Two-Factor Authentication (2FA)
|
||||||
@ -72,12 +76,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/* global siteConfig */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
name: 'John Doe'
|
name: 'John Doe'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
darkMode() { return siteConfig.darkMode }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
query {
|
query {
|
||||||
authentication {
|
authentication {
|
||||||
strategies {
|
strategies(orderBy: "title ASC") {
|
||||||
isEnabled
|
isEnabled
|
||||||
key
|
key
|
||||||
props
|
props
|
||||||
@ -10,6 +10,9 @@ query {
|
|||||||
key
|
key
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
selfRegistration
|
||||||
|
domainWhitelist
|
||||||
|
autoEnrollGroups
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import _ from 'lodash'
|
|||||||
|
|
||||||
/* global siteConfig, graphQL */
|
/* global siteConfig, graphQL */
|
||||||
|
|
||||||
import localeQuery from 'gql/common-locale-query.gql'
|
import localeQuery from 'gql/common/common-locale-query.gql'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
VueI18Next,
|
VueI18Next,
|
||||||
|
@ -39,12 +39,14 @@ module.exports = {
|
|||||||
_.pull(currentStrategies, 'session')
|
_.pull(currentStrategies, 'session')
|
||||||
_.forEach(currentStrategies, stg => { passport.unuse(stg) })
|
_.forEach(currentStrategies, stg => { passport.unuse(stg) })
|
||||||
|
|
||||||
// Load enable strategies
|
// Load enabled strategies
|
||||||
const enabledStrategies = await WIKI.db.authentication.getEnabledStrategies()
|
const enabledStrategies = await WIKI.db.authentication.getStrategies()
|
||||||
console.info(enabledStrategies)
|
|
||||||
for (let idx in enabledStrategies) {
|
for (let idx in enabledStrategies) {
|
||||||
const stg = enabledStrategies[idx]
|
const stg = enabledStrategies[idx]
|
||||||
|
if (!stg.isEnabled) { continue }
|
||||||
|
|
||||||
const strategy = require(`../modules/authentication/${stg.key}`)
|
const strategy = require(`../modules/authentication/${stg.key}`)
|
||||||
|
|
||||||
stg.config.callbackURL = `${WIKI.config.host}/login/${stg.key}/callback` // TODO: config.host
|
stg.config.callbackURL = `${WIKI.config.host}/login/${stg.key}/callback` // TODO: config.host
|
||||||
strategy.init(passport, stg.config)
|
strategy.init(passport, stg.config)
|
||||||
|
|
||||||
|
@ -31,6 +31,9 @@ exports.up = knex => {
|
|||||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||||
table.boolean('useForm').notNullable().defaultTo(false)
|
table.boolean('useForm').notNullable().defaultTo(false)
|
||||||
table.jsonb('config').notNullable()
|
table.jsonb('config').notNullable()
|
||||||
|
table.boolean('selfRegistration').notNullable().defaultTo(false)
|
||||||
|
table.jsonb('domainWhitelist').notNullable()
|
||||||
|
table.jsonb('autoEnrollGroups').notNullable()
|
||||||
})
|
})
|
||||||
// COMMENTS ----------------------------
|
// COMMENTS ----------------------------
|
||||||
.createTable('comments', table => {
|
.createTable('comments', table => {
|
||||||
|
@ -22,13 +22,21 @@ module.exports = class Authentication extends Model {
|
|||||||
title: {type: 'string'},
|
title: {type: 'string'},
|
||||||
isEnabled: {type: 'boolean'},
|
isEnabled: {type: 'boolean'},
|
||||||
useForm: {type: 'boolean'},
|
useForm: {type: 'boolean'},
|
||||||
config: {type: 'object'}
|
config: {type: 'object'},
|
||||||
|
selfRegistration: {type: 'boolean'},
|
||||||
|
domainWhitelist: {type: 'object'},
|
||||||
|
autoEnrollGroups: {type: 'object'}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getEnabledStrategies() {
|
static async getStrategies() {
|
||||||
return WIKI.db.authentication.query().where({ isEnabled: true })
|
const strategies = await WIKI.db.authentication.query()
|
||||||
|
return strategies.map(str => ({
|
||||||
|
...str,
|
||||||
|
domainWhitelist: _.get(str.domainWhitelist, 'v', []),
|
||||||
|
autoEnrollGroups: _.get(str.autoEnrollGroups, 'v', [])
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
static async refreshStrategiesFromDisk() {
|
static async refreshStrategiesFromDisk() {
|
||||||
@ -46,7 +54,10 @@ module.exports = class Authentication extends Model {
|
|||||||
config: _.reduce(strategy.props, (result, value, key) => {
|
config: _.reduce(strategy.props, (result, value, key) => {
|
||||||
_.set(result, value, '')
|
_.set(result, value, '')
|
||||||
return result
|
return result
|
||||||
}, {})
|
}, {}),
|
||||||
|
selfRegistration: false,
|
||||||
|
domainWhitelist: { v: [] },
|
||||||
|
autoEnrollGroups: { v: [] }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
AuthenticationQuery: {
|
AuthenticationQuery: {
|
||||||
async strategies(obj, args, context, info) {
|
async strategies(obj, args, context, info) {
|
||||||
let strategies = await WIKI.db.authentication.getEnabledStrategies()
|
let strategies = await WIKI.db.authentication.getStrategies()
|
||||||
strategies = strategies.map(stg => ({
|
strategies = strategies.map(stg => ({
|
||||||
...stg,
|
...stg,
|
||||||
config: _.transform(stg.config, (res, value, key) => {
|
config: _.transform(stg.config, (res, value, key) => {
|
||||||
|
@ -56,6 +56,9 @@ type AuthenticationStrategy {
|
|||||||
useForm: Boolean!
|
useForm: Boolean!
|
||||||
icon: String
|
icon: String
|
||||||
config: [KeyValuePair]
|
config: [KeyValuePair]
|
||||||
|
selfRegistration: Boolean!
|
||||||
|
domainWhitelist: [String]!
|
||||||
|
autoEnrollGroups: [String]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationLoginResponse {
|
type AuthenticationLoginResponse {
|
||||||
|
@ -24,7 +24,7 @@ module.exports = {
|
|||||||
return arr.filter(prvFilter.test)
|
return arr.filter(prvFilter.test)
|
||||||
},
|
},
|
||||||
orderBy (arr, orderString) {
|
orderBy (arr, orderString) {
|
||||||
let orderParams = _.zip(orderString.split(',').map(ord => _.trim(ord).split(' ').map(_.trim)))
|
let orderParams = _.zip(...orderString.split(',').map(ord => _.trim(ord).split(' ').map(_.trim)))
|
||||||
return _.orderBy(arr, orderParams[0], orderParams[1])
|
return _.orderBy(arr, orderParams[0], orderParams[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
key: 'google',
|
key: 'google',
|
||||||
title: 'Google ID',
|
title: 'Google',
|
||||||
useForm: false,
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret'],
|
props: ['clientId', 'clientSecret'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
|
Loading…
Reference in New Issue
Block a user