Merge pull request #580 from netlify/identity-widget

Authentication with Netlify Identity and Git Gateway
This commit is contained in:
Shawn Erquhart 2017-09-06 18:32:04 -07:00 committed by GitHub
commit caa5c69522
11 changed files with 99 additions and 43 deletions

View File

@ -1,6 +1,6 @@
{
"name": "netlify-cms",
"version": "0.5.0-beta.1",
"version": "0.5.0-beta.7",
"description": "Netlify CMS lets content editors work on structured content stored in git",
"main": "dist/cms.js",
"scripts": {
@ -68,6 +68,7 @@
"exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "^0.11.2",
"gotrue-js": "^0.9.3",
"identity-obj-proxy": "^3.0.0",
"imports-loader": "^0.7.1",
"jest": "^20.0.4",
@ -101,7 +102,6 @@
"dateformat": "^1.0.12",
"deep-equal": "^1.0.1",
"fuzzy": "^0.1.1",
"gotrue-js": "^0.9.3",
"history": "^2.1.2",
"immutability-helper": "^2.0.0",
"immutable": "^3.7.6",
@ -115,6 +115,7 @@
"mdast-util-definitions": "^1.2.2",
"mdast-util-to-string": "^1.0.4",
"moment": "^2.11.2",
"node-sass": "^3.10.0",
"normalize.css": "^4.2.0",
"preliminaries": "1.1.0",
"preliminaries-parser-toml": "1.1.0",

View File

@ -77,7 +77,8 @@ export function logoutUser() {
return (dispatch, getState) => {
const state = getState();
const backend = currentBackend(state.config);
backend.logout();
dispatch(logout());
Promise.resolve(backend.logout()).then(() => {
dispatch(logout());
});
};
}

View File

@ -1,7 +1,7 @@
import { attempt, isError } from 'lodash';
import TestRepoBackend from "./test-repo/implementation";
import GitHubBackend from "./github/implementation";
import NetlifyAuthBackend from "./netlify-auth/implementation";
import GitGatewayBackend from "./git-gateway/implementation";
import { resolveFormat } from "../formats/formats";
import { selectListMethod, selectEntrySlug, selectEntryPath, selectAllowNewEntries, selectFolderEntryExtension } from "../reducers/collections";
import { createEntry } from "../valueObjects/Entry";
@ -36,12 +36,12 @@ const slugFormatter = (template = "{{slug}}", entryData) => {
const identifier = identifiers.find(ident => ident !== undefined);
if (identifier === undefined) {
throw new Error("Collection must have a field name that is a valid entry identifier");
throw new Error("Collection must have a field name that is a valid entry identifier");
}
return identifier;
};
return template.replace(/\{\{([^\}]+)\}\}/g, (_, field) => {
switch (field) {
case "year":
@ -88,11 +88,11 @@ class Backend {
}
logout() {
if (this.authStore) {
this.authStore.logout();
} else {
throw new Error("User isn't authenticated.");
}
return Promise.resolve(this.implementation.logout()).then(() => {
if (this.authStore) {
this.authStore.logout();
}
});
}
getToken = () => this.implementation.getToken();
@ -292,8 +292,8 @@ export function resolveBackend(config) {
return new Backend(new TestRepoBackend(config), authStore);
case "github":
return new Backend(new GitHubBackend(config), authStore);
case "netlify-auth":
return new Backend(new NetlifyAuthBackend(config), authStore);
case "git-gateway":
return new Backend(new GitGatewayBackend(config), authStore);
default:
throw new Error(`Backend not found: ${ name }`);
}

View File

@ -1,28 +1,54 @@
import React from "react";
import Input from "react-toolbox/lib/input";
import Button from "react-toolbox/lib/button";
import { Notifs } from 'redux-notifications';
import { Toast } from '../../components/UI/index';
import { Card, Icon } from "../../components/UI";
import logo from "./netlify_logo.svg";
import styles from "./AuthenticationPage.css";
let component = null;
if (window.netlifyIdentity) {
window.netlifyIdentity.on('login', (user) => {
component && component.handleIdentityLogin(user);
});
window.netlifyIdentity.on('logout', () => {
component && component.handleIdentityLogout();
});
}
export default class AuthenticationPage extends React.Component {
constructor(props) {
super(props);
this.identity = window.netlifyIdentity;
this.state = {user: this.identity && this.identity.gotrue && this.identity.gotrue.currentUser()};
component = this;
}
componentDidMount() {
if (this.identity && !this.state.user) {
this.identity.on('login', (user) => {
this.props.onLogin(user);
this.identity.close();
});
this.identity.on('signup', (user) => {
this.props.onLogin(user);
this.identity.close();
});
this.identity.open();
if (!this.loggedIn && window.netlifyIdentity && window.netlifyIdentity.currentUser()) {
this.props.onLogin(window.netlifyIdentity.currentUser());
window.netlifyIdentity.close();
}
}
componentWillUnmount() {
component = null;
}
handleIdentityLogin = (user) => {
this.props.onLogin(user);
window.netlifyIdentity.close();
}
handleIdentityLogout = () => {
window.netlifyIdentity.open();
}
handleIdentity = () => {
if (window.netlifyIdentity.currentUser()) {
this.props.onLogin(user);
} else {
window.netlifyIdentity.open();
}
}
@ -66,6 +92,15 @@ export default class AuthenticationPage extends React.Component {
const { errors } = this.state;
const { error } = this.props;
if (window.netlifyIdentity) {
return <section className={styles.root}>
<Notifs CustomComponent={Toast} />
<Button className={styles.button} raised onClick={this.handleIdentity}>
Login with Netlify Identity
</Button>
</section>
}
return (
<section className={styles.root}>
<Card className={styles.card}>

View File

@ -11,38 +11,40 @@ const localHosts = {
'127.0.0.1': true,
'0.0.0.0': true
}
const defaults = {
identity: '/.netlify/identity',
gateway: '/.netlify/git/github'
}
function getEndpoint(endpoint, netlifySiteURL) {
if (localHosts[document.location.host] && netlifySiteURL && endpoint.match(/^\/\.netlify\//)) {
const parts = [netlifySiteURL];
if (!netlifySiteURL.match(/\/$/)) { parts.push("/"); }
parts.push(endpoint);
if (localHosts[document.location.host.split(":").shift()] && netlifySiteURL && endpoint.match(/^\/\.netlify\//)) {
const parts = [];
if (netlifySiteURL) {
parts.push(netlifySiteURL);
if (!netlifySiteURL.match(/\/$/)) { parts.push("/"); }
}
parts.push(endpoint.replace(/^\//, ''));
return parts.join("");
}
return endpoint;
}
export default class NetlifyAuth extends GitHubBackend {
export default class GitGateway extends GitHubBackend {
constructor(config) {
super(config, true);
if (config.getIn(["backend", "auth_url"]) == null) { throw new Error("The NetlifyAuth backend needs an \"auth_url\" in the backend configuration."); }
if (config.getIn(["backend", "github_proxy_url"]) == null) {
throw new Error("The NetlifyAuth backend needs an \"github_proxy_url\" in the backend configuration.");
}
this.accept_roles = (config.getIn(["backend", "accept_roles"]) || List()).toArray();
const netlifySiteURL = localStorage.getItem("netlifySiteURL");
const APIUrl = getEndpoint(config.getIn(["backend", "auth_url"]), netlifySiteURL);
this.github_proxy_url = getEndpoint(config.getIn(["backend", "github_proxy_url"]), netlifySiteURL);
this.authClient = new GoTrue({APIUrl});
const APIUrl = getEndpoint(config.getIn(["backend", "identity_url"], defaults.identity), netlifySiteURL);
this.github_proxy_url = getEndpoint(config.getIn(["backend", "gateway_url"], defaults.gateway), netlifySiteURL);
this.authClient = window.netlifyIdentity ? window.netlifyIdentity.gotrue : new GoTrue({APIUrl});
AuthenticationPage.authClient = this.authClient;
}
setUser() {
const user = this.authClient.currentUser();
const user = this.authClient && this.authClient.currentUser();
if (!user) return Promise.reject();
return this.authenticate(user);
}
@ -58,7 +60,7 @@ export default class NetlifyAuth extends GitHubBackend {
const userRoles = get(jwtDecode(token), 'app_metadata.roles', []);
if (validRole) {
const userData = {
name: user.user_metadata.name,
name: user.user_metadata.name || user.email.split('@').shift(),
email: user.email,
metadata: user.user_metadata,
};
@ -74,6 +76,14 @@ export default class NetlifyAuth extends GitHubBackend {
});
}
logout() {
if (window.netlifyIdentity) {
return window.netlifyIdentity.logout();
}
const user = this.authClient.currentUser();
return user && user.logout();
}
getToken() {
return this.tokenPromise();
}

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -42,6 +42,11 @@ export default class GitHub {
);
}
logout() {
this.token = null;
return;
}
getToken() {
return Promise.resolve(this.token);
}

View File

@ -2,8 +2,8 @@ import React from 'react';
import Input from "react-toolbox/lib/input";
import Button from "react-toolbox/lib/button";
import { Card, Icon } from "../../components/UI";
import logo from "../netlify-auth/netlify_logo.svg";
import styles from "../netlify-auth/AuthenticationPage.css";
import logo from "../git-gateway/netlify_logo.svg";
import styles from "../git-gateway/AuthenticationPage.css";
export default class AuthenticationPage extends React.Component {
static propTypes = {

View File

@ -36,6 +36,10 @@ export default class TestRepo {
return Promise.resolve({ email: state.email, name: nameFromEmail(state.email) });
}
logout() {
return null;
}
getToken() {
return Promise.resolve('');
}