它在开发环境中工作得很好,但在生产环境中不工作。
当您按下正面的登录按钮时,它是“ / auth / twitter”路线 应该经过认证。
这是我的前台代码
import React from "react";
import { Mutation } from "react-apollo";
import { CREATE_USER } from "../../queries";
import { Link, withRouter } from "react-router-dom";
import {
Grid,
Form,
Segment,
Button,
Header,
Message,
Icon,
Transition
} from "semantic-ui-react";
import "./Auth.css";
class Signup extends React.Component {
state = {
username: "",
email: "",
password: "",
passwordConfirmation: "",
errors: [],
onOpen: false
};
isFormValid = () => {
const errors = [];
let error;
let valid = true;
if (this.isFormEmpty(this.state)) {
error = { message: "全てのフィールドを埋めてください" };
this.setState({ errors: errors.concat(error) });
valid = false;
}
if (!this.isPasswordValid(this.state)) {
error = { message: "パスワードが不正です" };
this.setState({ errors: errors.concat(error) });
valid = false;
}
return valid;
};
isFormEmpty = ({ username, email, password, passwordConfirmation }) => {
return (
!username.length ||
!email.length ||
!password.length ||
!passwordConfirmation.length
);
};
isPasswordValid = ({ password, passwordConfirmation }) => {
if (password < 6) {
return false;
} else if (password !== passwordConfirmation) {
return false;
} else {
return true;
}
};
displayErrors = errors => {
return errors.map((error, i) => <p key={i}>{error.message}</p>);
};
handleChange = event => {
const { name, value } = event.target;
this.setState({ [name]: value });
};
handleSubmit = (event, createUser) => {
event.preventDefault();
if (this.isFormValid()) {
this.setState({ errors: [] });
createUser().then(async ({ data }) => {
localStorage.setItem("token", data.createUser.token);
await this.props.refetch();
});
}
};
handleInputError = (errors, inputName) => {
return errors.some(error => error.message.includes(inputName))
? "error"
: "";
};
render() {
const {
username,
email,
password,
passwordConfirmation,
errors,
onOpen
} = this.state;
return (
<Grid className="Auth" textAlign="center" verticalAlign="middle">
<Grid.Column style={{ maxWidth: 367 }}>
<Header as="h2" icon color="purple" textAlign="center">
<Icon name="new pied piper" color="purple" /> Manehabi 会員登録
</Header>
{onOpen ? null : (
<Mutation
mutation={CREATE_USER}
variables={{ username, email, password, passwordConfirmation }}
onCompleted={() => this.setState({ onOpen: true })}>
{(createUser, { data, loading, error }) => {
if (errors.length === 0) {
if (error) {
this.setState({ errors: error.graphQLErrors[0].data });
}
}
return (
<Form
size="large"
onSubmit={event => this.handleSubmit(event, createUser)}>
<Segment stacked>
<Form.Input
fluid
name="username"
icon="user"
iconPosition="left"
placeholder="ユーザ名"
onChange={this.handleChange}
value={username}
className={this.handleInputError(errors, "ユーザー")}
type="text"
/>
<Form.Input
fluid
name="email"
icon="mail"
iconPosition="left"
placeholder="Eメールアドレス"
onChange={this.handleChange}
value={email}
className={this.handleInputError(errors, "Eメール")}
type="email"
/>
<Form.Input
fluid
name="password"
icon="lock"
iconPosition="left"
placeholder="パスワード"
onChange={this.handleChange}
value={password}
className={this.handleInputError(errors, "パスワード")}
type="password"
/>
<Form.Input
fluid
name="passwordConfirmation"
icon="repeat"
iconPosition="left"
placeholder="パスワードの確認"
onChange={this.handleChange}
value={passwordConfirmation}
className={this.handleInputError(errors, "パスワード")}
type="password"
/>
<Button
disabled={loading}
className={loading ? "loading" : ""}
color="orange"
size="large"
fluid>
会員登録
</Button>
<Button
disabled={loading}
className={loading ? "loading" : ""}
color="twitter"
size="large"
style={{ margin: "1em 0 0 0" }}
fluid
as="a"
href="/auth/twitter">
<Icon name="twitter" /> Twitterで会員登録する
</Button>
</Segment>
</Form>
);
}}
</Mutation>
)}
{errors.length > 0 && (
<Message error>
<h3>エラー</h3>
{this.displayErrors(errors)}
</Message>
)}
{onOpen ? null : (
<Message>
既に会員登録済みの方は
<Link to="signin"> こちらからログイン</Link>
</Message>
)}
{/* success message */}
<Transition
animation="fade"
visible={onOpen}
duration="2000"
onComplete={() => this.props.history.push("/habits")}>
<Message icon success size="massive">
<Message.Content>
<Icon name="check" />
<Message.Header>会員登録完了</Message.Header>
ようこそ.Manehabiへ
</Message.Content>
</Message>
</Transition>
</Grid.Column>
</Grid>
);
}
}
export default withRouter(Signup);
这是我的后端代码
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const path = require("path");
const passport = require("passport");
const cookieParser = require("cookie-parser"); // parse cookie header
const cookieSession = require("cookie-session");
require("./config/passport");
const { typeDefs } = require("./typeDefs.graphql");
const { resolvers } = require("./resolvers");
const { ApolloServer } = require("apollo-server-express");
const app = express();
const createToken = (user, secret, expiresIn) => {
const { username, email } = user;
const token = jwt.sign({ username, email }, secret, { expiresIn });
return token;
};
const db = require("./config/keys").mongoURI;
const secret = require("./config/keys").secret;
const baseClientURL = require("./config/keys").baseClientURL;
const baseURL =
process.env.NODE_ENV === "production"
? "https://manehabi.herokuapp.com"
: "http://localhost:3000";
mongoose
.connect(db, { useNewUrlParser: true })
.then(() => console.log("MongoDB Connected"))
.catch(err => console.log(err));
const corsOption = {
origin: ["https://manehabi.herokuapp.com"],
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true
};
app.use(cors("*"));
app.use(passport.initialize());
const cookieKeys = require("./config/keys").cookieKeys;
app.use(
cookieSession({
name: "session",
keys: [cookieKeys],
maxAge: 24 * 60 * 60 * 100
})
);
app.use(cookieParser());
app.use(passport.session());
app.get("/return-json", (req, res, next) => {
res.redirect("/signup");
});
app.get(`/auth/twitter`, passport.authenticate("twitter"));
app.get(
"/auth/twitter/callback",
passport.authenticate("twitter", {
failureRedirect: "/signin"
}),
(req, res) => {
console.log(req.user);
const token = createToken(req.user, secret, "1hr");
res.redirect(`${baseURL}/habits?token=${token}`);
}
);
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
let token = null;
let currentUser = null;
try {
token = req.headers["authorization"];
if (token) {
currentUser = await jwt.verify(token, secret);
console.log("aaa");
}
} catch (err) {}
return { currentUser };
},
// playground: {
// endpoint: `http://localhost:4000/graphql`,
// settings: {
// "editor.theme": "light"
// }
// },
formatError(err) {
if (!err.originalError) {
return err;
}
const data = err.originalError.data;
const message = err.message || "何かエラーが発生しました。";
const code = err.originalError.code;
return { message, status: code, data };
}
});
server.applyMiddleware({ app });
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log(`Server listhening on PORT ${PORT}`);
});
在生产环境中,按下按钮将不会进行身份验证,仅跳转到URL不会发生任何反应。
这是推特的护照
const passport = require("passport");
const twitterStrategy = require("passport-twitter").Strategy;
const consumerKey = require("./keys").consumerKey;
const consumerSecret = require("./keys").consumerSecret;
const User = require("../models/User");
const url = require("./keys").baseClientURL;
// serialize the user.id to save in the cookie session
// so the browser will remember the user when login
passport.serializeUser((user, done) => {
done(null, user.id);
});
// deserialize the cookieUserId to user in the database
passport.deserializeUser((id, done) => {
User.findById(id)
.then(user => {
done(null, user);
})
.catch(e => {
done(new Error("Failed to deserialize an user"));
});
});
passport.use(
new twitterStrategy(
{
consumerKey,
consumerSecret,
callbackURL: `${url}/auth/twitter/callback`,
userProfileURL:
"https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true"
},
async (token, tokenSecret, profile, done) => {
//認証通過後の処理はここに書く
const { displayName, emails, photos, id, provider } = profile;
const existedUser = await User.findOne({ email: emails[0].value });
//callbackの呼び出し
if (!existedUser) {
const newUser = new User({
username: displayName,
email: emails[0].value,
imageUrl: photos[0].value,
provider: {
authId: id,
authType: provider,
token,
tokenSecret
}
});
await newUser.save();
done(null, newUser);
}
done(null, existedUser);
}
)
);