timscott / react-devise

A ReactJS front-end for a Ruby on Rails Devise authentication server.
MIT License
76 stars 12 forks source link

Omniauth Support? #2

Open g33kidd opened 7 years ago

g33kidd commented 7 years ago

Does this support Omniauth at the moment? Or is this planned?

timscott commented 7 years ago

It's planned. I've got some code in a side project that does oauth with Rails and jwt (not Devise or Omniauth) which I can use as a starting point. That said, the app I'm currently working on does not require social logins, so I'm not sure how soon I will get to it.

azeemh commented 7 years ago

Hi Tim, love what you're doing here, need a way to use app that supports OmniAuth login and reactjs... If there's anyway we can help get that moving or even contribute to the cause please lmk.

timscott commented 7 years ago

@azeemh - I've been planning to work on this. Help is certainly welcomed.

I have an side project where I'm doing OAuth with react and devise. I created that before this project. If I get a chance, I will create an oauth branch this weekend with some of that code as a starting point. From there you can fork it and help out maybe.

If it's urgent, and you want to start from scratch, go ahead and fork master now.

timscott commented 7 years ago

I created a branch: https://github.com/timscott/react-devise/tree/auth-provider-support. Basically it adds an action providerLogin which you will call from your view with a provider name and provider auth token.

I did not create a view, and I'm not sure I want to. There are probably many ways to create oauth buttons in react. There are various libraries that provide the basic building blocks, or you can roll your own. I'm not sure I want to take more dependencies and/or more opinions.

For reference, here is some code I pulled from another side project and which I started to integrate it into react-devise-sample. I started, but gave up for now. I was having some trouble with getting the token back from the server, and also, for this to work I would need to create Facebook and Google apps, which I don't want to trouble with now.

That said, this code might help as a starting point to create your own view. I'm also open to considering a default provider auth view for react-devise:

import React from 'react';
import FacebookLogin from 'react-facebook-login';
import GoogleLogin from 'react-google-login';
import {providerLogin} from 'react-devise/lib/actions';
import styled from 'styled-components';
import FontAwesome from 'react-fontawesome';
import {connect} from 'react-redux';
import {Flex, Box} from 'grid-styled';
import {Redirect} from 'react-router-dom';

const LoginContainer = styled(Flex)`
  margin-top: 100px;
`;

const LoginButtonContainer = styled.div`
  margin-bottom: 5px;
`;

const buttonStyles = `
  font-family: Helvetica, sans-serif;
  font-weight: 700;
  font-smoothing: antialiased;
  color: #fff;
  cursor: pointer;
  display: inline-block;
  font-size: calc(.27548vw + 12.71074px);
  text-decoration: none;
  text-transform: uppercase;
  transition: background-color .3s, border-color .3s;
  padding: calc(.34435vw + 3.38843px) calc(.34435vw + 8.38843px);
  width: 250px;
`;

const MyFacebookLogin = ({className, ...props}) => {
  return (
    <FacebookLogin cssClass={className} {...props} />
  );
};

const StyledFacebookLogin = styled(MyFacebookLogin)`
  ${buttonStyles}
  background-color: #4c69ba;
  border: calc(.06887vw + .67769px) solid #4c69ba;
`;

const StyledGoogleLogin = styled(GoogleLogin)`
  ${buttonStyles}
  background-color: #d1484d;
  border: calc(.06887vw + .67769px) solid #d1484d;
`;

const SocialIcon = styled(FontAwesome)`
  margin-right: 10px;
  font-size: 1.3em;
`;

const Alert = styled(Box)`
  background-color: #f3c4c9;
  margin-bottom: 10px;
  color: #aa2833;
  font-weight: bold;
  border: 1px solid #aa2833;
  border-radius: 3px;
  padding: 3px 10px;
`;

const mapStateToProps = state => {
  return {
    currentUser: state.currentUser
  };
};

const mapDispatchToProps = dispatch => {
  return {
    providerLogin: data => {
      return providerLogin(data, dispatch);
    }
  };
};

const withProviderAuth = (provider, Button) => {
  const ProviderAuth = ({providerLogin}) => {
    const login = accessToken => {
      return providerLogin({
        provider,
        accessToken
      });
    };
    const authenticate = response => {
      return login(response.accessToken);
    };
    const authenticateFailed = response => {
      console.log(`Authentication failed with ${provider}. ${response}`);
    };
    return <Button
      authenticate={authenticate}
      authenticateFailed={authenticateFailed}
    />;
  };
  return connect(mapStateToProps, mapDispatchToProps)(ProviderAuth);
};

const OauthFacebook = withProviderAuth('facebook', ({authenticate}) => {
  return (
    <StyledFacebookLogin
      appId={process.env.REACT_APP_FACEBOOK_APP_ID}
      fields=""
      callback={authenticate}
      icon={<SocialIcon name="facebook" />}
    />
  );
});

const OauthGoogle = withProviderAuth('google', ({authenticate, authenticateFailed}) => {
  return (
    <StyledGoogleLogin
      clientId={process.env.REACT_APP_GOOGLE_APP_ID}
      onSuccess={authenticate}
      onFailure={authenticateFailed}
    >
      <SocialIcon name="google" />
      <span>Login with Google</span>
    </StyledGoogleLogin>
  );
});

let OauthView = ({currentUser, location: {state: {flash, from: {pathname: returnTo} = {}} = {}} = {}}) => {
  if (currentUser) {
    if (currentUser.isLoggedIn) {
      return <Redirect to={returnTo || '/'} />;
    }
    if (currentUser.isLoggingIn) {
      return (
        <div>Logging in...</div>
      );
    }
  }
  return (
    <LoginContainer align="center" column>
      {flash && <Alert>{flash}</Alert>}
      <Box>
        <LoginButtonContainer>
          <OauthFacebook />
        </LoginButtonContainer>
        <LoginButtonContainer>
          <OauthGoogle />
        </LoginButtonContainer>
      </Box>
    </LoginContainer>
  );
};

OauthView = connect(mapStateToProps, mapDispatchToProps)(OauthView);

export {
  OauthView,
  OauthFacebook,
  OauthGoogle
};