├── .coveralls.yml ├── .babelrc ├── dummy ├── config │ ├── default.json │ └── production.json ├── src │ ├── actions │ │ ├── demo-ui.js │ │ └── request-test-buttons.js │ ├── styles │ │ └── main.scss │ ├── reducers │ │ ├── demo-ui.js │ │ └── request-test-buttons.js │ ├── views │ │ ├── partials │ │ │ ├── ExampleWell.js │ │ │ ├── IndexPanel.js │ │ │ ├── GlobalComponents.js │ │ │ ├── Container.js │ │ │ ├── CodeSnippet.js │ │ │ ├── RequestTestErrorModal.js │ │ │ ├── RequestTestSuccessModal.js │ │ │ └── RequestTestButton.js │ │ ├── Account.js │ │ └── SignIn.js │ ├── client.js │ └── server.js ├── babel.server.js ├── webpack.client-watch.js ├── webpack.client.js └── package.json ├── static └── favicon.ico ├── docs ├── gifs │ ├── sign-out.gif │ ├── email-sign-in.gif │ ├── email-sign-up.gif │ ├── oauth-sign-in.gif │ ├── destroy-account.gif │ ├── update-password.gif │ └── request-password-reset.gif ├── images │ ├── bs-sign-out.png │ ├── mui-sign-out.png │ ├── diagram-oauth.jpg │ ├── bs-email-sign-in.png │ ├── bs-email-sign-up.png │ ├── bs-oauth-sign-in.png │ ├── bs-password-reset.png │ ├── default-sign-out.png │ ├── diagram-sign-out.jpg │ ├── mui-email-sign-in.png │ ├── mui-email-sign-up.png │ ├── mui-error-dialog.png │ ├── mui-inline-errors.png │ ├── mui-oauth-sign-in.png │ ├── redux-auth-logo.gif │ ├── bs-destroy-account.png │ ├── bs-update-password.png │ ├── mui-destroy-account.png │ ├── mui-password-reset.png │ ├── mui-update-password.png │ ├── default-email-sign-in.png │ ├── default-email-sign-up.png │ ├── default-oauth-sign-in.png │ ├── default-password-reset.png │ ├── diagram-email-sign-in.jpg │ ├── diagram-email-sign-up.jpg │ ├── diagram-password-reset.jpg │ ├── default-destroy-account.png │ ├── default-update-password.png │ ├── diagram-destroy-account.jpg │ ├── diagram-token-validation.jpg │ └── diagram-update-password.jpg ├── psd │ └── redux-auth-logo.psd ├── diagrams │ ├── diagram-oauth.graffle │ ├── diagram-sign-out.graffle │ ├── diagram-email-sign-in.graffle │ ├── diagram-email-sign-up.graffle │ ├── diagram-destroy-account.graffle │ ├── diagram-password-reset.graffle │ ├── diagram-update-password.graffle │ └── diagram-token-validation.graffle └── api-expectations │ ├── sign-out.md │ ├── oauth-sign-in.md │ ├── destroy-account.md │ ├── request-password-reset.md │ ├── email-sign-up.md │ └── email-sign-in.md ├── .gitignore ├── .travis.yml ├── test ├── compiler.js └── runner.js ├── src ├── utils │ ├── constants.js │ ├── handle-fetch-response.js │ ├── popup.js │ ├── parse-endpoint-config.js │ └── fetch.js ├── actions │ ├── server.js │ ├── authenticate.js │ ├── sign-out.js │ ├── update-password.js │ ├── destroy-account.js │ ├── update-password-modal.js │ ├── email-sign-up.js │ ├── request-password-reset.js │ ├── email-sign-in.js │ └── oauth-sign-in.js ├── views │ ├── bootstrap │ │ ├── index.js │ │ ├── modals │ │ │ ├── EmailSignInErrorModal.js │ │ │ ├── SignOutSuccessModal.js │ │ │ ├── DestroyAccountErrorModal.js │ │ │ ├── EmailSignUpErrorModal.js │ │ │ ├── UpdatePasswordErrorModal.js │ │ │ ├── RequestPasswordResetErrorModal.js │ │ │ ├── FirstTimeLoginErrorModal.js │ │ │ ├── UpdatePasswordSuccessModal.js │ │ │ ├── OAuthSignInSuccessModal.js │ │ │ ├── DestroyAccountSuccessModal.js │ │ │ ├── EmailSignInSuccessModal.js │ │ │ ├── OAuthSignInErrorModal.js │ │ │ ├── FirstTimeLoginSuccessModal.js │ │ │ ├── SignOutErrorModal.js │ │ │ ├── RequestPasswordResetSuccessModal.js │ │ │ ├── EmailSignUpSuccessModal.js │ │ │ └── Modal.js │ │ ├── DestroyAccountButton.js │ │ ├── SignOutButton.js │ │ ├── ErrorList.js │ │ ├── Input.js │ │ ├── OAuthSignInButton.js │ │ ├── ButtonLoader.js │ │ ├── RequestPasswordResetForm.js │ │ ├── EmailSignInForm.js │ │ └── UpdatePasswordForm.js │ ├── default │ │ ├── index.js │ │ ├── modals │ │ │ ├── EmailSignInErrorModal.js │ │ │ ├── DestroyAccountErrorModal.js │ │ │ ├── SignOutSuccessModal.js │ │ │ ├── EmailSignUpErrorModal.js │ │ │ ├── UpdatePasswordErrorModal.js │ │ │ ├── RequestPasswordResetErrorModal.js │ │ │ ├── FirstTimeLoginErrorModal.js │ │ │ ├── UpdatePasswordSuccessModal.js │ │ │ ├── OAuthSignInSuccessModal.js │ │ │ ├── DestroyAccountSuccessModal.js │ │ │ ├── OAuthSignInErrorModal.js │ │ │ ├── EmailSignInSuccessModal.js │ │ │ ├── FirstTimeLoginSuccessModal.js │ │ │ ├── SignOutErrorModal.js │ │ │ ├── RequestPasswordResetSuccessModal.js │ │ │ ├── EmailSignUpSuccessModal.js │ │ │ └── Modal.js │ │ ├── DestroyAccountButton.js │ │ ├── SignOutButton.js │ │ ├── Input.js │ │ ├── ErrorList.js │ │ ├── OAuthSignInButton.js │ │ ├── ButtonLoader.js │ │ ├── RequestPasswordResetForm.js │ │ ├── EmailSignInForm.js │ │ └── UpdatePasswordForm.js │ ├── material-ui │ │ ├── index.js │ │ ├── modals │ │ │ ├── EmailSignInErrorModal.js │ │ │ ├── SignOutSuccessModal.js │ │ │ ├── DestroyAccountErrorModal.js │ │ │ ├── EmailSignUpErrorModal.js │ │ │ ├── UpdatePasswordErrorModal.js │ │ │ ├── RequestPasswordResetErrorModal.js │ │ │ ├── UpdatePasswordSuccessModal.js │ │ │ ├── FirstTimeLoginErrorModal.js │ │ │ ├── DestroyAccountSuccessModal.js │ │ │ ├── OAuthSignInSuccessModal.js │ │ │ ├── FirstTimeLoginSuccessModal.js │ │ │ ├── EmailSignInSuccessModal.js │ │ │ ├── RequestPasswordResetSuccessModal.js │ │ │ ├── SignOutErrorModal.js │ │ │ ├── EmailSignUpSuccessModal.js │ │ │ ├── OAuthSignInErrorModal.js │ │ │ └── Modal.js │ │ ├── DestroyAccountButton.js │ │ ├── SignOutButton.js │ │ ├── Input.js │ │ ├── OAuthSignInButton.js │ │ ├── ErrorList.js │ │ ├── RequestPasswordResetForm.js │ │ ├── UpdatePasswordForm.js │ │ └── EmailSignInForm.js │ └── TokenBridge.js ├── reducers │ ├── server.js │ ├── authenticate.js │ ├── destroy-account.js │ ├── oauth-sign-in.js │ ├── sign-out.js │ ├── configure.js │ ├── email-sign-in.js │ ├── email-sign-up.js │ ├── update-password.js │ ├── update-password-modal.js │ ├── request-password-reset.js │ └── user.js └── index.js ├── .editorconfig ├── LICENSE.md ├── .eslintrc └── CHANGELOG.md /.coveralls.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: ["es2015", "react", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /dummy/config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiUrl": "http://devise-token-auth.dev" 3 | } 4 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /docs/gifs/sign-out.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/sign-out.gif -------------------------------------------------------------------------------- /dummy/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiUrl": "http://devise-token-auth-demo.herokuapp.com" 3 | } 4 | -------------------------------------------------------------------------------- /docs/gifs/email-sign-in.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/email-sign-in.gif -------------------------------------------------------------------------------- /docs/gifs/email-sign-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/email-sign-up.gif -------------------------------------------------------------------------------- /docs/gifs/oauth-sign-in.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/oauth-sign-in.gif -------------------------------------------------------------------------------- /docs/images/bs-sign-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-sign-out.png -------------------------------------------------------------------------------- /docs/images/mui-sign-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-sign-out.png -------------------------------------------------------------------------------- /docs/psd/redux-auth-logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/psd/redux-auth-logo.psd -------------------------------------------------------------------------------- /docs/gifs/destroy-account.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/destroy-account.gif -------------------------------------------------------------------------------- /docs/gifs/update-password.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/update-password.gif -------------------------------------------------------------------------------- /docs/images/diagram-oauth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-oauth.jpg -------------------------------------------------------------------------------- /docs/images/bs-email-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-email-sign-in.png -------------------------------------------------------------------------------- /docs/images/bs-email-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-email-sign-up.png -------------------------------------------------------------------------------- /docs/images/bs-oauth-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-oauth-sign-in.png -------------------------------------------------------------------------------- /docs/images/bs-password-reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-password-reset.png -------------------------------------------------------------------------------- /docs/images/default-sign-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-sign-out.png -------------------------------------------------------------------------------- /docs/images/diagram-sign-out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-sign-out.jpg -------------------------------------------------------------------------------- /docs/images/mui-email-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-email-sign-in.png -------------------------------------------------------------------------------- /docs/images/mui-email-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-email-sign-up.png -------------------------------------------------------------------------------- /docs/images/mui-error-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-error-dialog.png -------------------------------------------------------------------------------- /docs/images/mui-inline-errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-inline-errors.png -------------------------------------------------------------------------------- /docs/images/mui-oauth-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-oauth-sign-in.png -------------------------------------------------------------------------------- /docs/images/redux-auth-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/redux-auth-logo.gif -------------------------------------------------------------------------------- /docs/diagrams/diagram-oauth.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-oauth.graffle -------------------------------------------------------------------------------- /docs/images/bs-destroy-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-destroy-account.png -------------------------------------------------------------------------------- /docs/images/bs-update-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/bs-update-password.png -------------------------------------------------------------------------------- /docs/images/mui-destroy-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-destroy-account.png -------------------------------------------------------------------------------- /docs/images/mui-password-reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-password-reset.png -------------------------------------------------------------------------------- /docs/images/mui-update-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/mui-update-password.png -------------------------------------------------------------------------------- /docs/diagrams/diagram-sign-out.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-sign-out.graffle -------------------------------------------------------------------------------- /docs/gifs/request-password-reset.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/gifs/request-password-reset.gif -------------------------------------------------------------------------------- /docs/images/default-email-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-email-sign-in.png -------------------------------------------------------------------------------- /docs/images/default-email-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-email-sign-up.png -------------------------------------------------------------------------------- /docs/images/default-oauth-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-oauth-sign-in.png -------------------------------------------------------------------------------- /docs/images/default-password-reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-password-reset.png -------------------------------------------------------------------------------- /docs/images/diagram-email-sign-in.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-email-sign-in.jpg -------------------------------------------------------------------------------- /docs/images/diagram-email-sign-up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-email-sign-up.jpg -------------------------------------------------------------------------------- /docs/images/diagram-password-reset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-password-reset.jpg -------------------------------------------------------------------------------- /docs/images/default-destroy-account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-destroy-account.png -------------------------------------------------------------------------------- /docs/images/default-update-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/default-update-password.png -------------------------------------------------------------------------------- /docs/images/diagram-destroy-account.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-destroy-account.jpg -------------------------------------------------------------------------------- /docs/images/diagram-token-validation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-token-validation.jpg -------------------------------------------------------------------------------- /docs/images/diagram-update-password.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/images/diagram-update-password.jpg -------------------------------------------------------------------------------- /docs/diagrams/diagram-email-sign-in.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-email-sign-in.graffle -------------------------------------------------------------------------------- /docs/diagrams/diagram-email-sign-up.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-email-sign-up.graffle -------------------------------------------------------------------------------- /docs/diagrams/diagram-destroy-account.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-destroy-account.graffle -------------------------------------------------------------------------------- /docs/diagrams/diagram-password-reset.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-password-reset.graffle -------------------------------------------------------------------------------- /docs/diagrams/diagram-update-password.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-update-password.graffle -------------------------------------------------------------------------------- /docs/diagrams/diagram-token-validation.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynndylanhurley/redux-auth/HEAD/docs/diagrams/diagram-token-validation.graffle -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | coverage/ 4 | dist/ 5 | build/ 6 | *.log 7 | *.map 8 | *.DS_Store 9 | npm-debug.log 10 | .tern-port 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.1" 4 | script: npm run test-coverage 5 | before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi 6 | -------------------------------------------------------------------------------- /test/compiler.js: -------------------------------------------------------------------------------- 1 | require("babel-polyfill"); 2 | 3 | require("babel-register")({ 4 | only: /(src|test)/, 5 | presets: ["es2015", "react", "stage-0"] 6 | }); 7 | -------------------------------------------------------------------------------- /src/utils/constants.js: -------------------------------------------------------------------------------- 1 | export const INITIAL_CONFIG_KEY = "default"; 2 | export const DEFAULT_CONFIG_KEY = "defaultConfigKey"; 3 | export const SAVED_CONFIG_KEY = "currentConfigName"; 4 | export const SAVED_CREDS_KEY = "authHeaders"; 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /src/utils/handle-fetch-response.js: -------------------------------------------------------------------------------- 1 | export function parseResponse (response) { 2 | let json = response.json(); 3 | if (response.status >= 200 && response.status < 300) { 4 | return json; 5 | } else { 6 | return json.then(err => Promise.reject(err)); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/actions/server.js: -------------------------------------------------------------------------------- 1 | export const SS_AUTH_TOKEN_UPDATE = "SS_AUTH_TOKEN_UPDATE"; 2 | 3 | export function ssAuthTokenUpdate({user, headers, mustResetPassword, firstTimeLogin, endpointKey}) { 4 | return { type: SS_AUTH_TOKEN_UPDATE, user, headers, mustResetPassword, firstTimeLogin, endpointKey }; 5 | } 6 | -------------------------------------------------------------------------------- /dummy/src/actions/demo-ui.js: -------------------------------------------------------------------------------- 1 | export const UPDATE_DEMO_THEME = "UPDATE_DEMO_THEME"; 2 | export const UPDATE_DEMO_ENDPOINT = "UPDATE_DEMO_ENDPOINT"; 3 | 4 | export function updateDemoTheme(theme) { 5 | return { type: UPDATE_DEMO_THEME, theme }; 6 | } 7 | 8 | export function updateDemoEndpoint(endpoint) { 9 | return { type: UPDATE_DEMO_ENDPOINT, endpoint }; 10 | } 11 | -------------------------------------------------------------------------------- /dummy/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; 2 | @import "~bootstrap-sass/assets/stylesheets/bootstrap"; 3 | @import "~highlight.js/styles/zenburn.css"; 4 | @import "~react-select/dist/react-select.css"; 5 | 6 | body { 7 | .code-snippet { 8 | margin-top: 10px; 9 | } 10 | 11 | .Select { 12 | margin-bottom: 10px; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dummy/src/reducers/demo-ui.js: -------------------------------------------------------------------------------- 1 | import Immutable from "immutable"; 2 | import { createReducer } from "redux-immutablejs"; 3 | import * as A from "../actions/demo-ui"; 4 | 5 | const initialState = Immutable.fromJS({ 6 | theme: "materialUi", 7 | endpoint: "default" 8 | }); 9 | 10 | export default createReducer(initialState, { 11 | [A.UPDATE_DEMO_THEME]: (state, {theme}) => state.merge({theme}), 12 | [A.UPDATE_DEMO_ENDPOINT]: (state, {endpoint}) => state.merge({endpoint}) 13 | }); 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /src/actions/authenticate.js: -------------------------------------------------------------------------------- 1 | export const AUTHENTICATE_START = "AUTHENTICATE_START"; 2 | export const AUTHENTICATE_COMPLETE = "AUTHENTICATE_COMPLETE"; 3 | export const AUTHENTICATE_ERROR = "AUTHENTICATE_ERROR"; 4 | 5 | export function authenticateStart() { 6 | return { type: AUTHENTICATE_START }; 7 | } 8 | export function authenticateComplete(user) { 9 | return { type: AUTHENTICATE_COMPLETE, user }; 10 | } 11 | export function authenticateError(errors) { 12 | return { type: AUTHENTICATE_ERROR, errors }; 13 | } 14 | -------------------------------------------------------------------------------- /dummy/src/views/partials/ExampleWell.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import { Panel } from "react-bootstrap"; 3 | 4 | class ExampleWell extends React.Component { 5 | static propTypes = { 6 | children: PropTypes.node.isRequired 7 | }; 8 | 9 | render () { 10 | return ( 11 |
12 | 13 | 14 | {this.props.children} 15 | 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default ExampleWell; 22 | -------------------------------------------------------------------------------- /src/views/bootstrap/index.js: -------------------------------------------------------------------------------- 1 | export AuthGlobals from "./AuthGlobals"; 2 | export EmailSignInForm from "./EmailSignInForm"; 3 | export EmailSignUpForm from "./EmailSignUpForm"; 4 | export SignOutButton from "./SignOutButton"; 5 | export RequestPasswordResetForm from "./RequestPasswordResetForm"; 6 | export OAuthSignInButton from "./OAuthSignInButton"; 7 | export UpdatePasswordForm from "./UpdatePasswordForm"; 8 | export DestroyAccountButton from "./DestroyAccountButton"; 9 | export TokenBridge from "../TokenBridge"; 10 | export ButtonLoader from "./ButtonLoader"; 11 | -------------------------------------------------------------------------------- /src/views/default/index.js: -------------------------------------------------------------------------------- 1 | export AuthGlobals from "./AuthGlobals"; 2 | export EmailSignInForm from "./EmailSignInForm"; 3 | export EmailSignUpForm from "./EmailSignUpForm"; 4 | export SignOutButton from "./SignOutButton"; 5 | export RequestPasswordResetForm from "./RequestPasswordResetForm"; 6 | export OAuthSignInButton from "./OAuthSignInButton"; 7 | export UpdatePasswordForm from "./UpdatePasswordForm"; 8 | export DestroyAccountButton from "./DestroyAccountButton"; 9 | export TokenBridge from "../TokenBridge"; 10 | export ButtonLoader from "./ButtonLoader"; 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "mocha": true, 7 | "jest": true 8 | }, 9 | "settings": { 10 | "ecmascript": 7, 11 | "jsx": true 12 | }, 13 | "plugins": [ 14 | "react" 15 | ], 16 | "rules": { 17 | "strict": 0, 18 | "quotes": 1, 19 | "no-unused-vars": 1, 20 | "camelcase": 0, 21 | "no-underscore-dangle": 0, 22 | "no-multi-spaces": 0, 23 | "key-spacing": 0 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/views/material-ui/index.js: -------------------------------------------------------------------------------- 1 | export AuthGlobals from "./AuthGlobals"; 2 | export EmailSignInForm from "./EmailSignInForm"; 3 | export EmailSignUpForm from "./EmailSignUpForm"; 4 | export SignOutButton from "./SignOutButton"; 5 | export RequestPasswordResetForm from "./RequestPasswordResetForm"; 6 | export OAuthSignInButton from "./OAuthSignInButton"; 7 | export UpdatePasswordForm from "./UpdatePasswordForm"; 8 | export DestroyAccountButton from "./DestroyAccountButton"; 9 | export TokenBridge from "../TokenBridge"; 10 | export ButtonLoader from "./ButtonLoader"; 11 | -------------------------------------------------------------------------------- /dummy/babel.server.js: -------------------------------------------------------------------------------- 1 | //require("babel-polyfill"); 2 | 3 | require("babel-core/register")({ 4 | only: /src/, 5 | presets: ["es2015", "react", "stage-0"] 6 | }); 7 | 8 | /** 9 | * Define isomorphic constants. 10 | */ 11 | global.__CLIENT__ = false; 12 | global.__SERVER__ = true; 13 | 14 | if (process.env.NODE_ENV !== "production") { 15 | if (!require("piping")({hook: true, includeModules: false})) { 16 | return; 17 | } 18 | } 19 | 20 | try { 21 | require("./src/server"); 22 | } 23 | catch (error) { 24 | console.error(error.stack); 25 | } 26 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/EmailSignInErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignInErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignInErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignInErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/EmailSignInErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignInErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignInErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignInErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/default/modals/EmailSignInErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignInErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignInErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignInErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/SignOutSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 | You have been successfully signed out. 14 | 15 | ); 16 | } 17 | } 18 | 19 | export default SignOutSuccessModal; 20 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/SignOutSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

You have been successfully signed out.

14 |
15 | ); 16 | } 17 | } 18 | 19 | export default SignOutSuccessModal; 20 | -------------------------------------------------------------------------------- /src/views/default/modals/DestroyAccountErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideDestroyAccountErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class DestroyAccountErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default DestroyAccountErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/default/modals/SignOutSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

You have been successfully signed out.

14 |
15 | ); 16 | } 17 | } 18 | 19 | export default SignOutSuccessModal; 20 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/DestroyAccountErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Modal from "./Modal"; 3 | import { hideDestroyAccountErrorModal } from "../../../actions/ui"; 4 | 5 | class DestroyAccountErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default DestroyAccountErrorModal; 19 | -------------------------------------------------------------------------------- /dummy/src/views/partials/IndexPanel.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import { Panel, Col } from "react-bootstrap"; 3 | 4 | class IndexPanel extends React.Component { 5 | static propTypes = { 6 | bsStyle: PropTypes.string, 7 | header: PropTypes.string, 8 | children: PropTypes.node 9 | }; 10 | 11 | static defaultProps = { 12 | bsStyle: "info", 13 | children: 14 | }; 15 | 16 | render () { 17 | return ( 18 | 19 | 20 | 21 | ); 22 | } 23 | } 24 | 25 | export default IndexPanel; 26 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/DestroyAccountErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideDestroyAccountErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class DestroyAccountErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default DestroyAccountErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/default/modals/EmailSignUpErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignUpErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignUpErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/EmailSignUpErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignUpErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignUpErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/EmailSignUpErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class EmailSignUpErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default EmailSignUpErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/UpdatePasswordErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default UpdatePasswordErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/UpdatePasswordErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default UpdatePasswordErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/default/modals/UpdatePasswordErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default UpdatePasswordErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/RequestPasswordResetErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hidePasswordResetRequestErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class RequestPasswordResetErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default RequestPasswordResetErrorModal; 19 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/UpdatePasswordSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

Your password has been successfully changed.

14 |
15 | ); 16 | } 17 | } 18 | 19 | export default UpdatePasswordSuccessModal; 20 | -------------------------------------------------------------------------------- /src/views/default/modals/RequestPasswordResetErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hidePasswordResetRequestErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class RequestPasswordResetErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default RequestPasswordResetErrorModal; 19 | 20 | -------------------------------------------------------------------------------- /dummy/src/views/Account.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PageHeader } from "react-bootstrap"; 3 | import { connect } from "react-redux"; 4 | import { SignOutButton } from "../../../src/views/bootstrap"; 5 | import { browserHistory } from "react-router"; 6 | 7 | class Account extends React.Component { 8 | render () { 9 | return ( 10 |
11 | Account page 12 |

This page should only visible to authenticated users.

13 | browserHistory.push("/")} /> 14 |
15 | ); 16 | } 17 | } 18 | 19 | export default connect(({auth}) => ({auth}))(Account); 20 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/RequestPasswordResetErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hidePasswordResetRequestErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class RequestPasswordResetErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 14 | ); 15 | } 16 | } 17 | 18 | export default RequestPasswordResetErrorModal; 19 | 20 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/FirstTimeLoginErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideFirstTimeLoginErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class FirstTimeLoginErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

There was a problem confirming your account. Please try again.

14 |
15 | ); 16 | } 17 | } 18 | 19 | export default FirstTimeLoginErrorModal; 20 | -------------------------------------------------------------------------------- /dummy/src/views/SignIn.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PageHeader } from "react-bootstrap"; 3 | import { connect } from "react-redux"; 4 | import { EmailSignInForm } from "../../../src/views/bootstrap"; 5 | import { browserHistory } from "react-router"; 6 | 7 | class SignIn extends React.Component { 8 | render () { 9 | return ( 10 |
11 | Sign In 12 | 13 | browserHistory.push("/account")} 15 | endpoint={this.props.pageEndpoint} /> 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default connect(({routes}) => ({routes}))(SignIn); 22 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/FirstTimeLoginErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideFirstTimeLoginErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class FirstTimeLoginErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

There was a problem confirming your account. Please try again.

14 |
15 | 16 | ); 17 | } 18 | } 19 | 20 | export default FirstTimeLoginErrorModal; 21 | -------------------------------------------------------------------------------- /src/views/default/modals/FirstTimeLoginErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideFirstTimeLoginErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class FirstTimeLoginErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

There was a problem confirming your account. Please try again.

14 |
15 | 16 | ); 17 | } 18 | } 19 | 20 | export default FirstTimeLoginErrorModal; 21 | -------------------------------------------------------------------------------- /src/views/default/modals/UpdatePasswordSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

14 | Your password has been successfully changed. 15 |

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default UpdatePasswordSuccessModal; 22 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/UpdatePasswordSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideUpdatePasswordSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class UpdatePasswordSuccessModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

14 | Your password has been successfully changed. 15 |

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default UpdatePasswordSuccessModal; 22 | -------------------------------------------------------------------------------- /src/reducers/server.js: -------------------------------------------------------------------------------- 1 | import Immutable from "immutable"; 2 | import { createReducer } from "redux-immutablejs"; 3 | import * as A from "../actions/server"; 4 | 5 | const initialState = Immutable.fromJS({ 6 | user: null, 7 | headers: null, 8 | firstTimeLogin: false, 9 | mustResetPassword: false 10 | }); 11 | 12 | export default createReducer(initialState, { 13 | [A.SS_AUTH_TOKEN_UPDATE]: (state, { 14 | user = null, 15 | headers = null, 16 | mustResetPassword = false, 17 | firstTimeLogin = false 18 | }) => { 19 | return state.merge({ 20 | user, 21 | headers, 22 | mustResetPassword, 23 | firstTimeLogin 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /src/reducers/authenticate.js: -------------------------------------------------------------------------------- 1 | import Immutable from "immutable"; 2 | import { createReducer } from "redux-immutablejs"; 3 | import * as A from "../actions/authenticate"; 4 | 5 | const initialState = Immutable.fromJS({ 6 | loading: false, 7 | valid: false, 8 | errors: null 9 | }); 10 | 11 | export default createReducer(initialState, { 12 | [A.AUTHENTICATE_START]: state => state.set("loading", true), 13 | 14 | [A.AUTHENTICATE_COMPLETE]: (state) => { 15 | return state.merge({ 16 | loading: false, 17 | errors: null, 18 | valid: true 19 | }); 20 | }, 21 | 22 | [A.AUTHENTICATE_ERROR]: state => state.merge({ 23 | loading: false, 24 | errors: "Invalid token", 25 | valid: false 26 | }) 27 | }); 28 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/OAuthSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideOAuthSignInSuccessModal } from "../../../actions/ui"; 3 | import { connect } from "react-redux"; 4 | import Modal from "./Modal" 5 | 6 | class OAuthSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

You are now signed in via {this.props.auth.getIn(["user", "attributes", "provider"])}.

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(OAuthSignInSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/default/modals/OAuthSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideOAuthSignInSuccessModal } from "../../../actions/ui"; 3 | import { connect } from "react-redux"; 4 | import Modal from "./Modal" 5 | 6 | class OAuthSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

You are now signed in via {this.props.auth.getIn(["user", "attributes", "provider"])}.

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(OAuthSignInSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/DestroyAccountSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Modal from "./Modal"; 3 | import { connect } from "react-redux"; 4 | import { hideDestroyAccountSuccessModal } from "../../../actions/ui"; 5 | 6 | class DestroyAccountSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

{this.props.auth.getIn(["ui", "destroyAccountMessage"])}

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(DestroyAccountSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/OAuthSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideOAuthSignInSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class OAuthSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

You are now signed in via {this.props.auth.getIn(["user", "attributes", "provider"])}.

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(OAuthSignInSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/DestroyAccountSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideDestroyAccountSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class DestroyAccountSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

{this.props.auth.getIn(["ui", "destroyAccountMessage"])}

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(DestroyAccountSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/default/modals/DestroyAccountSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideDestroyAccountSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class DestroyAccountSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

{this.props.auth.getIn(["ui", "destroyAccountMessage"])}

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(DestroyAccountSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/FirstTimeLoginSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideFirstTimeLoginSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class FirstTimeLoginSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

Your account has been confirmed.

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(FirstTimeLoginSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/default/modals/OAuthSignInErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Glyphicon } from "react-bootstrap"; 3 | import Modal from "./Modal"; 4 | import { hideOAuthSignInErrorModal } from "../../../actions/ui"; 5 | 6 | class OAuthSignInErrorModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | There was an error 16 | authenticating your account. Please try again. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default OAuthSignInErrorModal; 24 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/EmailSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideEmailSignInSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class EmailSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 15 |

You are now signed in as {this.props.auth.getIn(["user", "attributes", "email"])}.

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default connect(({auth}) => ({auth}))(EmailSignInSuccessModal); 22 | 23 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/OAuthSignInErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Glyphicon } from "react-bootstrap"; 3 | import Modal from "./Modal"; 4 | import { hideOAuthSignInErrorModal } from "../../../actions/ui"; 5 | 6 | class OAuthSignInErrorModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | There was an error 16 | authenticating your account. Please try again. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default OAuthSignInErrorModal; 24 | -------------------------------------------------------------------------------- /src/views/default/modals/EmailSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hideEmailSignInSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class EmailSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 15 |

You are now signed in as {this.props.auth.getIn(["user", "attributes", "email"])}.

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default connect(({auth}) => ({auth}))(EmailSignInSuccessModal); 22 | 23 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/EmailSignInSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Modal from "./Modal"; 3 | import { connect } from "react-redux"; 4 | import { hideEmailSignInSuccessModal } from "../../../actions/ui"; 5 | 6 | class EmailSignInSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | You are now signed in 16 | as {this.props.auth.getIn(["user", "attributes", "email"])}. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default connect(({auth}) => ({auth}))(EmailSignInSuccessModal); 24 | -------------------------------------------------------------------------------- /dummy/src/client.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { initialize } from "./app"; 4 | 5 | 6 | /** 7 | * Fire-up React Router. 8 | */ 9 | const reactRoot = window.document.getElementById("react-root"); 10 | initialize().then(({provider}) => { 11 | ReactDOM.render(provider, reactRoot); 12 | }); 13 | 14 | 15 | /** 16 | * Detect whether the server-side render has been discarded due to an invalid checksum. 17 | */ 18 | if (process.env.NODE_ENV !== "production") { 19 | if (!reactRoot.firstChild || !reactRoot.firstChild.attributes || 20 | !reactRoot.firstChild.attributes["data-react-checksum"]) { 21 | console.error("Server-side React render was discarded. Make sure that your initial render does not contain any client-side code."); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/views/default/modals/FirstTimeLoginSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideFirstTimeLoginSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | import { connect } from "react-redux"; 5 | 6 | class FirstTimeLoginSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 15 |

Your account has been confirmed.

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default connect(({auth}) => ({auth}))(FirstTimeLoginSuccessModal); 22 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/RequestPasswordResetSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hidePasswordResetRequestSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class RequestPasswordResetSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

{this.props.auth.getIn(["ui", "requestPasswordResetSuccessMessage"])}

15 |
16 | ); 17 | } 18 | } 19 | 20 | export default connect(({auth}) => ({auth}))(RequestPasswordResetSuccessModal); 21 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/FirstTimeLoginSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideFirstTimeLoginSuccessModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | import { connect } from "react-redux"; 5 | 6 | class FirstTimeLoginSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 15 |

Your account has been confirmed.

16 |
17 | ); 18 | } 19 | } 20 | 21 | export default connect(({auth}) => ({auth}))(FirstTimeLoginSuccessModal); 22 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/SignOutErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

14 | The server encountered an error while trying to sign you out. Your 15 | account information has been wiped from this browser, but you may 16 | want to sign in and then sign back out again to resolve any issues. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default SignOutErrorModal; 24 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/SignOutErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

14 | The server encountered an error while trying to sign you out. Your 15 | account information has been wiped from this browser, but you may 16 | want to sign in and then sign back out again to resolve any issues. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default SignOutErrorModal; 24 | -------------------------------------------------------------------------------- /src/views/default/modals/SignOutErrorModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideSignOutErrorModal } from "../../../actions/ui"; 3 | import Modal from "./Modal"; 4 | 5 | class SignOutErrorModal extends React.Component { 6 | render () { 7 | return ( 8 | 13 |

14 | The server encountered an error while trying to sign you out. Your 15 | account information has been wiped from this browser, but you may 16 | want to sign in and then sign back out again to resolve any issues. 17 |

18 |
19 | ); 20 | } 21 | } 22 | 23 | export default SignOutErrorModal; 24 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/RequestPasswordResetSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hidePasswordResetRequestSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class RequestPasswordResetSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | {this.props.auth.getIn(["ui", "requestPasswordResetSuccessMessage"])} 16 |

17 |
18 | ); 19 | } 20 | } 21 | 22 | export default connect(({auth}) => ({auth}))(RequestPasswordResetSuccessModal); 23 | -------------------------------------------------------------------------------- /src/views/default/modals/RequestPasswordResetSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | import { hidePasswordResetRequestSuccessModal } from "../../../actions/ui"; 4 | import Modal from "./Modal"; 5 | 6 | class RequestPasswordResetSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | {this.props.auth.getIn(["ui", "requestPasswordResetSuccessMessage"])} 16 |

17 |
18 | ); 19 | } 20 | } 21 | 22 | export default connect(({auth}) => ({auth}))(RequestPasswordResetSuccessModal); 23 | -------------------------------------------------------------------------------- /src/views/material-ui/modals/EmailSignUpSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpSuccessModal } from "../../../actions/ui"; 3 | import { connect } from "react-redux"; 4 | import Modal from "./Modal"; 5 | 6 | class EmailSignUpSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | A confirmation email was sent to your account 16 | at {this.props.auth.getIn(["ui", "emailSignUpAddress"])}. Follow the 17 | instructions in the email to complete registration. 18 |

19 |
20 | ); 21 | } 22 | } 23 | 24 | export default connect(({auth}) => ({auth}))(EmailSignUpSuccessModal); 25 | -------------------------------------------------------------------------------- /src/views/bootstrap/modals/EmailSignUpSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpSuccessModal } from "../../../actions/ui"; 3 | import { connect } from "react-redux"; 4 | import Modal from "./Modal"; 5 | 6 | class EmailSignUpSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | A confirmation email was sent to your account 16 | at {this.props.auth.getIn(["ui", "emailSignUpAddress"])}. Follow the 17 | instructions in the email to complete registration. 18 |

19 |
20 | ); 21 | } 22 | } 23 | 24 | export default connect(({auth}) => ({auth}))(EmailSignUpSuccessModal); 25 | -------------------------------------------------------------------------------- /src/views/default/modals/EmailSignUpSuccessModal.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { hideEmailSignUpSuccessModal } from "../../../actions/ui"; 3 | import { connect } from "react-redux"; 4 | import Modal from "./Modal"; 5 | 6 | class EmailSignUpSuccessModal extends React.Component { 7 | render () { 8 | return ( 9 | 14 |

15 | A confirmation email was sent to your account 16 | at {this.props.auth.getIn(["ui", "emailSignUpAddress"])}. Follow the 17 | instructions in the email to complete registration. 18 |

19 |
20 | ); 21 | } 22 | } 23 | 24 | export default connect(({auth}) => ({auth}))(EmailSignUpSuccessModal); 25 | -------------------------------------------------------------------------------- /src/views/TokenBridge.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { connect } from "react-redux"; 3 | 4 | class TokenBridge extends React.Component { 5 | render () { 6 | return ( 7 | 31 | ${styles} 32 | 33 | 34 |
${markup}
35 | 36 | 37 | `; 38 | } 39 | 40 | /** 41 | * Start Hapi server on port 8000. 42 | */ 43 | const server = new Server(); 44 | 45 | server.connection({host: hostname, port: process.env.PORT || 8000}); 46 | 47 | server.register([ 48 | h2o2, 49 | inert 50 | ], function (err) { 51 | if (err) { 52 | throw err; 53 | } 54 | 55 | server.start(function () { 56 | console.info("==> ✅ Server is listening"); 57 | console.info("==> 🌎 Go to " + server.info.uri.toLowerCase()); 58 | }); 59 | }); 60 | 61 | 62 | /** 63 | * Attempt to serve static requests from the public folder. 64 | */ 65 | server.route({ 66 | method: "GET", 67 | path: "/{params*}", 68 | handler: { 69 | file: (request) => "static" + request.path 70 | } 71 | }); 72 | 73 | 74 | /** 75 | * Catch dynamic requests here to fire-up React Router. 76 | */ 77 | server.ext("onPreResponse", (request, reply) => { 78 | if (typeof request.response.statusCode !== "undefined") { 79 | return reply.continue(); 80 | } 81 | 82 | var query = qs.stringify(request.query); 83 | var location = request.path + (query.length ? "?" + query : ""); 84 | 85 | initialize({ 86 | isServer: true, 87 | cookies: request.headers.cookie, 88 | currentLocation: location, 89 | userAgent: request.headers["user-agent"] 90 | }) 91 | .then(({provider, blank, routes, history, location}) => { 92 | match({routes, history}, (error, redirectLocation, renderProps) => { 93 | if (redirectLocation) { 94 | reply.redirect(redirectLocation.pathname + redirectLocation.search); 95 | } else if (error || !renderProps) { 96 | reply.continue(); 97 | } else { 98 | var webserver = process.env.NODE_ENV === "production" ? "" : "//" + hostname + ":8080"; 99 | var output = (blank) ? "" : getMarkup(webserver, provider); 100 | 101 | reply(output); 102 | } 103 | }); 104 | }).catch(e => console.log("@-->server error", e, e.stack)); 105 | }); 106 | -------------------------------------------------------------------------------- /dummy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-auth", 3 | "description": "Token authentication for redux with isomorphic support.", 4 | "version": "0.0.1-alpha1", 5 | "license": "BSD-3-Clause", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/lynndylanhurley/redux-auth.git" 9 | }, 10 | "homepage": "https://github.com/lynndylanhurley/redux-auth", 11 | "keywords": [ 12 | "react", 13 | "isomorphic", 14 | "universal", 15 | "starter", 16 | "boilerplate", 17 | "template", 18 | "webpack", 19 | "hapi", 20 | "transmit" 21 | ], 22 | "main": "babel.server.js", 23 | "scripts": { 24 | "start": "NODE_PATH=\"./src\" node --harmony ./babel.server", 25 | "build": "node ./node_modules/webpack/bin/webpack.js --verbose --colors --display-error-details --config webpack.client.js", 26 | "watch-client": "node ./node_modules/webpack/bin/webpack.js --verbose --colors --display-error-details --config webpack.client-watch.js && node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config webpack.client-watch.js", 27 | "watch": "node ./node_modules/concurrently/src/main.js --kill-others \"npm run watch-client\" \"npm run start\"" 28 | }, 29 | "dependencies": { 30 | "babel-core": "6.3.13", 31 | "babel-polyfill": "6.3.13", 32 | "babel-preset-es2015": "6.3.13", 33 | "babel-preset-react": "6.3.13", 34 | "babel-preset-stage-0": "6.3.13", 35 | "babel-register": "6.3.13", 36 | "bootstrap": "^3.3.5", 37 | "bootstrap-sass": "^3.3.5", 38 | "bootstrap-webpack": "0.0.5", 39 | "classnames": "^2.1.5", 40 | "config": "^1.17.1", 41 | "cookie": "^0.2.2", 42 | "exports-loader": "^0.6.2", 43 | "extend": "^3.0.0", 44 | "h2o2": "4.0.1", 45 | "hapi": "9.3.1", 46 | "highlight.js": "^8.8.0", 47 | "immutable": "^3.7.5", 48 | "imports-loader": "^0.6.4", 49 | "inert": "3.0.1", 50 | "isomorphic-fetch": "2.1.1", 51 | "jquery-deparam": "^0.4.2", 52 | "less": "^2.5.3", 53 | "less-loader": "^2.2.1", 54 | "node-sass": "^3.3.3", 55 | "piping": "0.3.0", 56 | "query-string": "^2.4.2", 57 | "react": "^15.2.1", 58 | "react-bootstrap": "^0.29.5", 59 | "react-dom": "^15.2.1", 60 | "react-inline-css": "2.0.0", 61 | "react-loader": "^2.0.0", 62 | "react-redux": "^4.4.0", 63 | "react-router": "^2.5.2", 64 | "react-router-bootstrap": "^0.23.0", 65 | "react-router-redux": "^4.0.5", 66 | "react-select": "^1.0.0-beta13", 67 | "react-tap-event-plugin": "^1.0.0", 68 | "react-transmit": "3.0.8", 69 | "redux": "^3.3.1", 70 | "redux-immutablejs": "0.0.6", 71 | "redux-thunk": "^1.0.0", 72 | "serialize-javascript": "^1.1.2", 73 | "thunk": "0.0.1", 74 | "url-loader": "^0.5.6", 75 | "whatwg-fetch": "^0.9.0" 76 | }, 77 | "devDependencies": { 78 | "babel-loader": "6.1.0", 79 | "concurrently": "0.1.1", 80 | "css-loader": "^0.19.0", 81 | "extract-text-webpack-plugin": "0.9.1", 82 | "file-loader": "0.8.5", 83 | "json-loader": "0.5.4", 84 | "react-hot-loader": "1.3.0", 85 | "redux-devtools": "^2.1.5", 86 | "sass-loader": "3.1.2", 87 | "style-loader": "^0.12.4", 88 | "webpack": "1.12.9", 89 | "webpack-dev-server": "1.14.0" 90 | }, 91 | "engines": { 92 | "node": ">=0.10.32" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /docs/api-expectations/email-sign-in.md: -------------------------------------------------------------------------------- 1 | # Email Sign In 2 | 3 | * [Overview](#overview) 4 | * [Request](#request) 5 | * [Request Body](#request-body) 6 | * [Request Headers](#request-headers) 7 | * [Success Response](#success-response) 8 | * [Success Response Body](#success-response-body) 9 | * [Success Response Headers](#success-response-headers) 10 | * [Error Response](#error-response) 11 | * [Error Response Body](#error-response-body) 12 | * [Error Response Headers](#error-response-headers) 13 | 14 | ## Overview 15 | 16 | ![email sign in](https://github.com/lynndylanhurley/redux-auth/raw/master/docs/images/diagram-email-sign-in.jpg) 17 | 18 | ## Request 19 | 20 | ##### method 21 | ###### POST 22 | 23 | ### Request Body 24 | 25 | ##### email 26 | ###### string 27 | The email address of the user trying to sign in. 28 | 29 | ##### password 30 | ###### string 31 | The password of the user trying to sign in 32 | 33 | -- 34 | 35 | ##### Request Body Example 36 | 37 | ~~~json 38 | { 39 | "email": "test@test.com", 40 | "password": "secret123" 41 | } 42 | ~~~ 43 | 44 | -- 45 | 46 | ### Request Headers 47 | 48 | None. The API won't care about any headers for this request. 49 | 50 | ## Success Response 51 | 52 | ### Success Response Body 53 | 54 | This be an object containing the attributes that your API is configured to send describing your user. The attributes will be nested within a `data` object. At a minimum, this should contain the following attributes. 55 | 56 | ##### uid 57 | ###### string 58 | The unique identifier for the user's account. 59 | 60 | ##### provider 61 | ###### string 62 | The account provider type (email, github, facebook, etc.). 63 | 64 | -- 65 | 66 | ##### Success Response Body Example 67 | 68 | ~~~json 69 | "data": { 70 | "uid": "test@test.com", 71 | "provider": "email", 72 | "email": "test@test.com", 73 | "favorite_color": null, 74 | "id": 6 75 | } 76 | ~~~ 77 | 78 | -- 79 | 80 | #### Success Response Headers 81 | 82 | ##### access-token 83 | ###### string 84 | The access token acts as a password for each request. 85 | 86 | ##### client 87 | ###### string 88 | The client token is used to identify device (browser client, phone, tablet, etc) of the current session. This allows us to maintain multiple concurrent sessions across devices / browsers. 89 | 90 | ##### expiry 91 | ###### integer 92 | The time at which the token will expire. 93 | 94 | ##### uid 95 | ###### string 96 | The unique identifier for the current user. 97 | 98 | -- 99 | 100 | ##### Success Response Headers Example 101 | 102 | ~~~ 103 | access-token: bgINB4atOxd8SMNvtOTDxg 104 | client: V7EN7LSRYAbpE_-c5PvRSw 105 | expiry: 1450988710 106 | uid: test@test.com 107 | ~~~ 108 | 109 | -- 110 | 111 | ## Error Response 112 | 113 | This will an array containing any errors that the server encountered in processing the request. 114 | 115 | ### Error Response Body 116 | 117 | ##### errors 118 | ###### array of strings 119 | 120 | A list of errors that will be displayed to the user. 121 | 122 | -- 123 | 124 | ##### Error Response Body Example 125 | ~~~json 126 | { 127 | "errors": ["Invalid login credentials. Please try again."] 128 | } 129 | ~~~ 130 | 131 | -- 132 | 133 | ### Error Response Headers 134 | None. The client won't care about any headers for this response. 135 | -------------------------------------------------------------------------------- /src/views/default/EmailSignInForm.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import ButtonLoader from "./ButtonLoader"; 3 | import Input from "./Input"; 4 | import { emailSignInFormUpdate, emailSignIn } from "../../actions/email-sign-in"; 5 | import { connect } from "react-redux"; 6 | 7 | class EmailSignInForm extends React.Component { 8 | static propTypes = { 9 | endpoint: PropTypes.string, 10 | next: PropTypes.func, 11 | inputProps: PropTypes.shape({ 12 | email: PropTypes.object, 13 | password: PropTypes.object, 14 | submit: PropTypes.object 15 | }) 16 | }; 17 | 18 | static defaultProps = { 19 | next: () => {}, 20 | inputProps: { 21 | email: {}, 22 | password: {}, 23 | submit: {} 24 | } 25 | }; 26 | 27 | getEndpoint () { 28 | return ( 29 | this.props.endpoint || 30 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 31 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 32 | ); 33 | } 34 | 35 | handleInput (key, val) { 36 | this.props.dispatch(emailSignInFormUpdate(this.getEndpoint(), key, val)); 37 | } 38 | 39 | handleSubmit (event) { 40 | event.preventDefault(); 41 | let formData = this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form"]).toJS(); 42 | this.props.dispatch(emailSignIn(formData, this.getEndpoint())) 43 | .then(this.props.next) 44 | .catch(() => {}); 45 | } 46 | 47 | render () { 48 | let disabled = ( 49 | this.props.auth.getIn(["user", "isSignedIn"]) || 50 | this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "loading"]) 51 | ); 52 | 53 | return ( 54 |
57 | 65 | 66 | 74 | 75 | 84 | Sign In 85 | 86 |
87 | ); 88 | } 89 | } 90 | 91 | export default connect(({auth}) => ({auth}))(EmailSignInForm); 92 | -------------------------------------------------------------------------------- /src/actions/oauth-sign-in.js: -------------------------------------------------------------------------------- 1 | import * as C from "../utils/constants"; 2 | import {getAllParams, normalizeTokenKeys} from "../utils/parse-url"; 3 | import {getOAuthUrl} from "../utils/session-storage"; 4 | import { 5 | setCurrentEndpointKey, 6 | getCurrentEndpointKey, 7 | getTokenValidationPath, 8 | persistData, 9 | } from "../utils/session-storage"; 10 | import {storeCurrentEndpointKey} from "./configure"; 11 | import {parseResponse} from "../utils/handle-fetch-response"; 12 | import fetch from "../utils/fetch"; 13 | import _openPopup from "../utils/popup"; 14 | 15 | export const OAUTH_SIGN_IN_START = "OAUTH_SIGN_IN_START"; 16 | export const OAUTH_SIGN_IN_COMPLETE = "OAUTH_SIGN_IN_COMPLETE"; 17 | export const OAUTH_SIGN_IN_ERROR = "OAUTH_SIGN_IN_ERROR"; 18 | 19 | // hook for rewire 20 | var openPopup = _openPopup; 21 | 22 | function listenForCredentials (endpointKey, popup, provider, resolve, reject) { 23 | if (!resolve) { 24 | return new Promise((resolve, reject) => { 25 | listenForCredentials(endpointKey, popup, provider, resolve, reject); 26 | }); 27 | 28 | } else { 29 | let creds; 30 | 31 | try { 32 | creds = getAllParams(popup.location); 33 | } catch (err) {} 34 | 35 | if (creds && creds.uid) { 36 | popup.close(); 37 | persistData(C.SAVED_CREDS_KEY, normalizeTokenKeys(creds)); 38 | fetch(getTokenValidationPath(endpointKey)) 39 | .then(parseResponse) 40 | .then(({data}) => resolve(data)) 41 | .catch(({errors}) => reject({errors})); 42 | } else if (popup.closed) { 43 | reject({errors: "Authentication was cancelled."}) 44 | } else { 45 | setTimeout(() => { 46 | listenForCredentials(endpointKey, popup, provider, resolve, reject); 47 | }, 0); 48 | } 49 | } 50 | } 51 | 52 | 53 | function authenticate({endpointKey, provider, url, tab=false}) { 54 | let name = (tab) ? "_blank" : provider; 55 | let popup = openPopup(provider, url, name); 56 | return listenForCredentials(endpointKey, popup, provider); 57 | } 58 | 59 | 60 | export function oAuthSignInStart(provider, endpoint) { 61 | return { type: OAUTH_SIGN_IN_START, provider, endpoint }; 62 | } 63 | export function oAuthSignInComplete(user, endpoint) { 64 | return { type: OAUTH_SIGN_IN_COMPLETE, user, endpoint }; 65 | } 66 | export function oAuthSignInError(errors, endpoint) { 67 | return { type: OAUTH_SIGN_IN_ERROR, errors, endpoint }; 68 | } 69 | export function oAuthSignIn({provider, params, endpointKey}) { 70 | return dispatch => { 71 | // save previous endpoint key in case of failure 72 | var prevEndpointKey = getCurrentEndpointKey(); 73 | 74 | // necessary for `fetch` to recognize the response as an api request 75 | setCurrentEndpointKey(endpointKey); 76 | dispatch(storeCurrentEndpointKey(endpointKey)); 77 | 78 | var currentEndpointKey = getCurrentEndpointKey(); 79 | 80 | dispatch(oAuthSignInStart(provider, currentEndpointKey)); 81 | 82 | let url = getOAuthUrl({provider, params, currentEndpointKey}); 83 | 84 | return authenticate({endpointKey, provider, url}) 85 | .then(user => dispatch(oAuthSignInComplete(user, currentEndpointKey))) 86 | .catch(({ errors }) => { 87 | // revert endpoint key to what it was before failed request 88 | setCurrentEndpointKey(prevEndpointKey); 89 | dispatch(storeCurrentEndpointKey(prevEndpointKey)); 90 | dispatch(oAuthSignInError(errors, currentEndpointKey)) 91 | throw errors; 92 | }); 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /src/views/bootstrap/EmailSignInForm.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from "react"; 2 | import ButtonLoader from "./ButtonLoader"; 3 | import Input from "./Input"; 4 | import { emailSignInFormUpdate, emailSignIn } from "../../actions/email-sign-in"; 5 | import { Glyphicon } from "react-bootstrap"; 6 | import { connect } from "react-redux"; 7 | 8 | class EmailSignInForm extends React.Component { 9 | static propTypes = { 10 | endpoint: PropTypes.string, 11 | next: PropTypes.func, 12 | inputProps: PropTypes.shape({ 13 | email: PropTypes.object, 14 | password: PropTypes.object, 15 | submit: PropTypes.object 16 | }) 17 | }; 18 | 19 | static defaultProps = { 20 | next: () => {}, 21 | inputProps: { 22 | email: {}, 23 | password: {}, 24 | submit: {} 25 | } 26 | }; 27 | 28 | getEndpoint () { 29 | return ( 30 | this.props.endpoint || 31 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 32 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 33 | ); 34 | } 35 | 36 | handleInput (key, val) { 37 | this.props.dispatch(emailSignInFormUpdate(this.getEndpoint(), key, val)); 38 | } 39 | 40 | handleSubmit (event) { 41 | event.preventDefault(); 42 | let formData = this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form"]).toJS(); 43 | this.props.dispatch(emailSignIn(formData, this.getEndpoint())) 44 | .then(this.props.next) 45 | .catch(() => {}); 46 | } 47 | 48 | render () { 49 | let disabled = ( 50 | this.props.auth.getIn(["user", "isSignedIn"]) || 51 | this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "loading"]) 52 | ); 53 | 54 | return ( 55 |
57 | 66 | 67 | 76 | 77 | } 80 | className='email-sign-in-submit pull-right' 81 | disabled={disabled} 82 | onClick={this.handleSubmit.bind(this)} 83 | {...this.props.inputProps.submit}> 84 | Sign In 85 | 86 |
87 | ); 88 | } 89 | } 90 | 91 | export default connect(({auth}) => ({auth}))(EmailSignInForm); 92 | -------------------------------------------------------------------------------- /src/views/material-ui/UpdatePasswordForm.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import Input from "./Input"; 3 | import ButtonLoader from "./ButtonLoader"; 4 | import ActionLock from "material-ui/svg-icons/action/lock"; 5 | import { updatePassword, updatePasswordFormUpdate } from "../../actions/update-password"; 6 | import { connect } from "react-redux"; 7 | 8 | class UpdatePasswordForm extends React.Component { 9 | static propTypes = { 10 | endpoint: PropTypes.string, 11 | inputProps: PropTypes.shape({ 12 | password: PropTypes.object, 13 | passwordConfirmation: PropTypes.object, 14 | submit: PropTypes.object 15 | }) 16 | }; 17 | 18 | static defaultProps = { 19 | inputProps: { 20 | password: {}, 21 | passwordConfirmation: {}, 22 | submit: {} 23 | } 24 | }; 25 | 26 | getEndpoint () { 27 | return ( 28 | this.props.endpoint || 29 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 30 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 31 | ); 32 | } 33 | 34 | handleInput (key, val) { 35 | this.props.dispatch(updatePasswordFormUpdate(this.getEndpoint(), key, val)); 36 | } 37 | 38 | handleSubmit (ev) { 39 | ev.preventDefault(); 40 | let formData = this.props.auth.getIn(["updatePassword", this.getEndpoint(), "form"]).toJS(); 41 | this.props.dispatch(updatePassword(formData, this.getEndpoint())); 42 | } 43 | 44 | render () { 45 | let endpoint = this.getEndpoint(); 46 | let loading = this.props.auth.getIn(["updatePassword", endpoint, "loading"]); 47 | let disabled = ( 48 | !this.props.auth.getIn(["user", "isSignedIn"]) || loading || 49 | (this.props.auth.getIn(["user", "attributes", "provider"]) !== "email") 50 | ); 51 | 52 | return ( 53 |
56 | 65 | 66 | 75 | 76 | 86 | Update Password 87 | 88 |
89 | ); 90 | } 91 | } 92 | 93 | export default connect(({auth}) => ({auth}))(UpdatePasswordForm); 94 | -------------------------------------------------------------------------------- /src/views/default/UpdatePasswordForm.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import Input from "./Input"; 3 | import ButtonLoader from "./ButtonLoader"; 4 | import { updatePassword, updatePasswordFormUpdate } from "../../actions/update-password"; 5 | import { connect } from "react-redux"; 6 | 7 | class UpdatePasswordForm extends React.Component { 8 | static propTypes = { 9 | icon: PropTypes.string, 10 | endpoint: PropTypes.string, 11 | inputProps: PropTypes.shape({ 12 | password: PropTypes.object, 13 | passwordConfirmation: PropTypes.object, 14 | submit: PropTypes.object 15 | }) 16 | }; 17 | 18 | static defaultProps = { 19 | inputProps: { 20 | password: {}, 21 | passwordConfirmation: {}, 22 | submit: {} 23 | } 24 | }; 25 | 26 | getEndpoint () { 27 | return ( 28 | this.props.endpoint || 29 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 30 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 31 | ); 32 | } 33 | 34 | handleInput (key, val) { 35 | this.props.dispatch(updatePasswordFormUpdate(this.getEndpoint(), key, val)); 36 | } 37 | 38 | handleSubmit (event) { 39 | event.preventDefault(); 40 | let formData = this.props.auth.getIn(["updatePassword", this.getEndpoint(), "form"]).toJS(); 41 | this.props.dispatch(updatePassword(formData, this.getEndpoint())); 42 | } 43 | 44 | render () { 45 | let endpoint = this.getEndpoint(); 46 | let loading = this.props.auth.getIn(["updatePassword", "loading"]); 47 | let disabled = ( 48 | !this.props.auth.getIn(["user", "isSignedIn"]) || loading || 49 | (this.props.auth.getIn(["user", "attributes", "provider"]) !== "email") 50 | ); 51 | 52 | return ( 53 |
56 | 66 | 67 | 77 | 78 | 88 | Update Password 89 | 90 |
91 | ); 92 | } 93 | } 94 | 95 | export default connect(({auth}) => ({auth}))(UpdatePasswordForm); 96 | -------------------------------------------------------------------------------- /src/views/bootstrap/UpdatePasswordForm.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import Input from "./Input"; 3 | import ButtonLoader from "./ButtonLoader"; 4 | import { Glyphicon } from "react-bootstrap"; 5 | import { updatePassword, updatePasswordFormUpdate } from "../../actions/update-password"; 6 | import { connect } from "react-redux"; 7 | 8 | class UpdatePasswordForm extends React.Component { 9 | static propTypes = { 10 | endpoint: PropTypes.string, 11 | inputProps: PropTypes.shape({ 12 | password: PropTypes.object, 13 | passwordConfirmation: PropTypes.object 14 | }) 15 | }; 16 | 17 | static defaultProps = { 18 | inputProps: { 19 | password: {}, 20 | passwordConfirmation: {} 21 | } 22 | }; 23 | 24 | getEndpoint () { 25 | return ( 26 | this.props.endpoint || 27 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 28 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 29 | ); 30 | } 31 | 32 | 33 | handleInput (key, val) { 34 | this.props.dispatch(updatePasswordFormUpdate(this.getEndpoint(), key, val)); 35 | } 36 | 37 | handleSubmit (event) { 38 | event.preventDefault(); 39 | let formData = this.props.auth.getIn(["updatePassword", this.getEndpoint(), "form"]).toJS(); 40 | this.props.dispatch(updatePassword(formData, this.getEndpoint())); 41 | } 42 | 43 | render () { 44 | let endpoint = this.getEndpoint(); 45 | let loading = this.props.auth.getIn(["updatePassword", endpoint, "loading"]); 46 | let disabled = ( 47 | !this.props.auth.getIn(["user", "isSignedIn"]) || loading || 48 | (this.props.auth.getIn(["user", "attributes", "provider"]) !== "email") 49 | ); 50 | 51 | return ( 52 |
54 | 63 | 64 | 73 | 74 | } 79 | onClick={this.handleSubmit.bind(this)} 80 | {...this.props.inputProps.submit}> 81 | Update Password 82 | 83 |
84 | ); 85 | } 86 | } 87 | 88 | export default connect(({auth}) => ({auth}))(UpdatePasswordForm); 89 | -------------------------------------------------------------------------------- /src/views/material-ui/EmailSignInForm.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from "react"; 2 | import ButtonLoader from "./ButtonLoader"; 3 | import Input from "./Input"; 4 | import { emailSignInFormUpdate, emailSignIn } from "../../actions/email-sign-in"; 5 | import ActionExitToApp from "material-ui/svg-icons/action/exit-to-app"; 6 | import { connect } from "react-redux"; 7 | 8 | class EmailSignInForm extends React.Component { 9 | static propTypes = { 10 | endpoint: PropTypes.string, 11 | next: PropTypes.func, 12 | inputProps: PropTypes.shape({ 13 | email: PropTypes.object, 14 | password: PropTypes.object, 15 | submit: PropTypes.object 16 | }) 17 | }; 18 | 19 | static defaultProps = { 20 | next: () => {}, 21 | inputProps: { 22 | email: {}, 23 | password: {}, 24 | submit: {} 25 | } 26 | }; 27 | 28 | getEndpoint () { 29 | return ( 30 | this.props.endpoint || 31 | this.props.auth.getIn(["configure", "currentEndpointKey"]) || 32 | this.props.auth.getIn(["configure", "defaultEndpointKey"]) 33 | ); 34 | } 35 | 36 | handleInput (key, val) { 37 | this.props.dispatch(emailSignInFormUpdate(this.getEndpoint(), key, val)); 38 | } 39 | 40 | handleSubmit (event) { 41 | event.preventDefault(); 42 | let formData = this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form"]).toJS(); 43 | this.props.dispatch(emailSignIn(formData, this.getEndpoint())) 44 | .then(this.props.next) 45 | .catch(() => {}); 46 | } 47 | 48 | render () { 49 | let disabled = ( 50 | this.props.auth.getIn(["user", "isSignedIn"]) || 51 | this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "loading"]) 52 | ); 53 | 54 | return ( 55 |
58 | 67 | 68 | 76 | 77 | 86 | Sign In 87 | 88 |
89 | ); 90 | } 91 | } 92 | 93 | export default connect(({auth}) => ({auth}))(EmailSignInForm); 94 | --------------------------------------------------------------------------------