Skip to content

Commit 3fbe208

Browse files
authored
Merge pull request #8 from howtographql/frontend-auth
Frontend Authentication is set up
2 parents b9d73b7 + 73637a8 commit 3fbe208

File tree

12 files changed

+173
-22
lines changed

12 files changed

+173
-22
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"private": true,
99
"scripts": {
1010
"lerna": "lerna",
11+
"dev": "npm-run-all --parallel 'site develop' 'server dev'",
1112
"site": "yarn workspace @howtographql/oss",
1213
"server": "yarn workspace @howtographql/server",
1314
"theme": "yarn workspace @howtographql/gatsby-theme",

packages/gatsby-theme/config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
type Config = {
22
apiUrl: string;
3+
githubClientId: string;
34
}
45

56
const config: Config = {
6-
apiUrl: process.env.GATSBY_API_URL || "http://localhost:4000"
7+
apiUrl: process.env.GATSBY_API_URL || "http://localhost:4000",
8+
githubClientId: process.env.GATSBY_GITHUB_CLIENT_ID || "Iv1.5345771c55b8eb37"
79
}
810

911
export default config;

packages/gatsby-theme/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"@types/react-copy-to-clipboard": "^4.2.6",
4747
"apollo-cache-inmemory": "^1.5.1",
4848
"apollo-client": "^2.5.1",
49+
"apollo-link-context": "^1.0.17",
4950
"apollo-link-http": "^1.5.14",
5051
"babel-plugin-styled-components": "^1.10.0",
5152
"gatsby-image": "^2.0.33",

packages/gatsby-theme/src/Apollo.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import { ApolloClient } from 'apollo-client';
2+
import { setContext } from 'apollo-link-context';
23
import { HttpLink } from 'apollo-link-http';
34
import { InMemoryCache } from 'apollo-cache-inmemory';
45
import fetch from 'isomorphic-fetch';
56
import config from '../config';
7+
import { getAuthToken } from './utils/auth';
68

9+
const authLink = setContext((_, { headers }) => {
10+
// get the authentication token from local storage if it exists
11+
const token = getAuthToken();
12+
// return the headers to the context so httpLink can read them
13+
return {
14+
headers: {
15+
...headers,
16+
authorization: token ? `Bearer ${token}` : "",
17+
}
18+
}
19+
});
20+
const httpLink = new HttpLink({ uri: config.apiUrl, fetch });
721
export const client = new ApolloClient({
8-
link: new HttpLink({ uri: config.apiUrl, fetch }),
22+
link: authLink.concat(httpLink),
923
cache: new InMemoryCache(),
1024
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { Component } from "react";
2+
import config from '../../config';
3+
import { authenticateUser } from "../utils/auth";
4+
5+
export default class GithubAuth extends Component {
6+
popup: any;
7+
listen(): Promise<string> {
8+
return new Promise(resolve => {
9+
window.addEventListener("message", receiveMessage, false);
10+
function receiveMessage(event: any) {
11+
if (event.data) {
12+
window.removeEventListener("message", receiveMessage);
13+
resolve(event.data)
14+
}
15+
}
16+
})
17+
}
18+
19+
openPopup() {
20+
const width = 600,
21+
height = 600;
22+
const left = window.innerWidth / 2 - width / 2;
23+
const top = window.innerHeight / 2 - height / 2;
24+
const url = `https://github.com/login/oauth/authorize?scope=user:email&client_id=${
25+
config.githubClientId
26+
}`;
27+
return window.open(
28+
url,
29+
"",
30+
`toolbar=no, location=no, directories=no, status=no, menubar=no,
31+
scrollbars=no, resizable=no, copyhistory=no, width=${width},
32+
height=${height}, top=${top}, left=${left}`
33+
);
34+
}
35+
36+
startAuth = async () => {
37+
this.popup = this.openPopup();
38+
const code = await this.listen();
39+
try {
40+
await authenticateUser(code)
41+
} catch (e) {
42+
console.error(e);
43+
}
44+
this.popup.close();
45+
};
46+
47+
render() {
48+
return (
49+
<button onClick={() => this.startAuth()}> Log in </button>
50+
);
51+
}
52+
}

packages/gatsby-theme/src/components/shared/Header.tsx

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import React from "react";
2+
import { Query } from 'react-apollo';
3+
import gql from 'graphql-tag';
4+
import GithubAuth from "../GithubAuth";
25

36
// Vectors
47
import Logo from "./Logo";
@@ -54,7 +57,28 @@ const Nav = withTheme(
5457
<NavLink to="/community">Community</NavLink>
5558
<NavLink to="/components">Components</NavLink>
5659
</Container>
57-
<Container justifyContent="flex-end">other stuff</Container>
60+
<Container justifyContent="flex-end">
61+
<Query query={gql`
62+
query Viewer {
63+
viewer {
64+
id
65+
user {
66+
id
67+
}
68+
}
69+
}
70+
`}>
71+
{({ data, error, loading }) => {
72+
if (error || loading) {
73+
return "Loading or error..."
74+
}
75+
if (data.viewer && data.viewer.user) {
76+
return data.viewer.user.id
77+
}
78+
return <GithubAuth />
79+
}}
80+
</Query>
81+
</Container>
5882
</InnerWrapper>
5983
</Wrapper>
6084
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as React from 'react';
2+
3+
function getParameterByName(name: string, url: string) {
4+
if (!url) url = window.location.href;
5+
name = name.replace(/[\[\]]/g, "\\$&");
6+
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
7+
results = regex.exec(url);
8+
if (!results) return null;
9+
if (!results[2]) return "";
10+
return decodeURIComponent(results[2].replace(/\+/g, " "));
11+
}
12+
13+
function Callback() {
14+
let githubCode = getParameterByName("code", window.location.href);
15+
let targetWindow = window.opener;
16+
targetWindow.postMessage(githubCode, "*");
17+
}
18+
19+
export default class AuthCallback extends React.Component {
20+
componentDidMount() {
21+
Callback()
22+
}
23+
render() {
24+
return <div style={{backgroundColor: "tomato"}}>
25+
Success!!
26+
</div>
27+
}
28+
}

packages/gatsby-theme/src/pages/index.tsx

-15
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,11 @@
11
import { RouterProps } from '@reach/router';
22
import * as React from 'react';
3-
import { Query } from 'react-apollo'
4-
import gql from 'graphql-tag';
5-
63
import Layout from '../components/layout';
74
import Listing from '../components/listing';
85
import { GlobalStyles } from '../styles';
96

107
const IndexPage: React.FunctionComponent<RouterProps> = ({ location }) => (
118
<Layout location={location}>
12-
<Query query={gql`
13-
query Test {
14-
feed {
15-
id
16-
}
17-
}
18-
`}>
19-
{({ data, error, loading }) => {
20-
console.log(data, error, loading)
21-
return <div />
22-
}}
23-
</Query>
249
<GlobalStyles />
2510
<Listing />
2611
</Layout>
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import gql from 'graphql-tag';
2+
import { client } from "../Apollo";
3+
4+
const TOKEN_KEY = "token";
5+
6+
const AUTHENTICATE_USER_MUTATION = gql`
7+
mutation AuthenticateUser($code: String!) {
8+
authenticate(githubCode: $code) {
9+
token
10+
}
11+
}
12+
`;
13+
export const authenticateUser = async (code: string) => {
14+
const res = await client.mutate({
15+
mutation: AUTHENTICATE_USER_MUTATION,
16+
variables: {
17+
code
18+
}
19+
})
20+
return localStorage.setItem(TOKEN_KEY, res.data.authenticate.token);
21+
}
22+
23+
export const getAuthToken = () => {
24+
return localStorage.getItem('token');
25+
}

packages/server/src/graphql/Viewer.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { getUserId } from "../utils";
44
export const Viewer = objectType({
55
name: "Viewer",
66
definition: (t) => {
7+
t.id("id");
78
t.field("user", {
89
type: "User",
9-
resolve: async (_, args, ctx) => {
10-
const id = getUserId(ctx)
10+
resolve: async ({ id }, args, ctx) => {
1111
return await ctx.prisma.user({ id })
1212
}
1313
})
@@ -16,5 +16,15 @@ export const Viewer = objectType({
1616

1717
export const viewer = queryField("viewer", {
1818
type: Viewer,
19-
resolve: () => ({})
19+
nullable: true,
20+
resolve: (_, args, ctx) => {
21+
try {
22+
const id = getUserId(ctx)
23+
return {
24+
id
25+
}
26+
} catch (e) {
27+
return null;
28+
}
29+
}
2030
})

packages/server/src/schema.graphql

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ input PostWhereInput {
9797
type Query {
9898
feed: [Post!]!
9999
filterPosts(searchString: String!): [Post!]!
100-
viewer: Viewer!
100+
viewer: Viewer
101101
}
102102

103103
type User {
@@ -237,5 +237,6 @@ input UserWhereInput {
237237
}
238238

239239
type Viewer {
240+
id: ID!
240241
user: User!
241242
}

yarn.lock

+8
Original file line numberDiff line numberDiff line change
@@ -1840,6 +1840,14 @@ apollo-graphql@^0.1.0:
18401840
apollo-env "0.4.0"
18411841
lodash.sortby "^4.7.0"
18421842

1843+
apollo-link-context@^1.0.17:
1844+
version "1.0.17"
1845+
resolved "https://registry.npmjs.org/apollo-link-context/-/apollo-link-context-1.0.17.tgz#439272cfb43ec1891506dd175ed907845b7de36c"
1846+
integrity sha512-W5UUfHcrrlP5uqJs5X1zbf84AMXhPZGAqX/7AQDgR6wY/7//sMGfJvm36KDkpIeSOElztGtM9z6zdPN1NbT41Q==
1847+
dependencies:
1848+
apollo-link "^1.2.11"
1849+
tslib "^1.9.3"
1850+
18431851
apollo-link-dedup@^1.0.0:
18441852
version "1.0.18"
18451853
resolved "https://registry.npmjs.org/apollo-link-dedup/-/apollo-link-dedup-1.0.18.tgz#635cb5659b082e7f270f7649c4b0f71021f7bb4b"

0 commit comments

Comments
 (0)