├── .gitignore
├── client
├── .buckconfig
├── .eslintignore
├── .eslintrc
├── .flowconfig
├── .gitignore
├── .watchmanconfig
├── index.android.js
├── index.ios.js
├── package.json
└── views
│ ├── HomeView.js
│ ├── LoginView.js
│ ├── ProtectedView.js
│ └── RegisterView.js
└── server
├── app.js
├── bin
└── www
├── models
└── user.js
├── package.json
├── public
└── stylesheets
│ └── style.css
├── routes
├── index.js
└── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | client/android/
3 | client/ios/
4 |
--------------------------------------------------------------------------------
/client/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/client/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage/**
2 | node_modules/**
3 | dist/**
4 | *.spec.js
5 | src/index.html
6 | app.js
7 |
--------------------------------------------------------------------------------
/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser" : "babel-eslint",
3 | "extends" : "airbnb",
4 | "env" : {
5 | "browser" : true
6 | },
7 | "globals" : {
8 | "__DEV__" : false,
9 | "__PROD__" : false,
10 | "__DEBUG__" : false,
11 | "__DEBUG_NEW_WINDOW__" : false,
12 | "__BASENAME__" : false,
13 | "React": true,
14 | "ReactDOM": true
15 | },
16 | "rules": {
17 | "semi" : [2, "never"],
18 | "react-in-jsx-scope": 0,
19 | "comma-dangle": [1, "never"],
20 | "func-names": 0,
21 | "indent": [1, 2],
22 | "jsx-quotes": [2, "prefer-single"],
23 | "key-spacing": 0,
24 | "new-cap": 0,
25 | "no-else-return": 0,
26 | "no-multi-spaces": 0,
27 | "no-shadow": 1,
28 | "no-unused-vars": 1,
29 | "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
30 | "one-var": 0,
31 | "padded-blocks": 1,
32 | "quotes": [2, "single"],
33 | "space-before-function-paren": [0, "never"],
34 | "spaced-comment": 1,
35 | "object-curly-spacing": [1, "always"],
36 | "quote-props": [1, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
37 | "react/jsx-no-bind": 1
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/client/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | # We fork some components by platform.
4 | .*/*.web.js
5 | .*/*.android.js
6 |
7 | # Some modules have their own node_modules with overlap
8 | .*/node_modules/node-haste/.*
9 |
10 | # Ugh
11 | .*/node_modules/babel.*
12 | .*/node_modules/babylon.*
13 | .*/node_modules/invariant.*
14 |
15 | # Ignore react and fbjs where there are overlaps, but don't ignore
16 | # anything that react-native relies on
17 | .*/node_modules/fbjs/lib/Map.js
18 | .*/node_modules/fbjs/lib/fetch.js
19 | .*/node_modules/fbjs/lib/ExecutionEnvironment.js
20 | .*/node_modules/fbjs/lib/ErrorUtils.js
21 |
22 | # Flow has a built-in definition for the 'react' module which we prefer to use
23 | # over the currently-untyped source
24 | .*/node_modules/react/react.js
25 | .*/node_modules/react/lib/React.js
26 | .*/node_modules/react/lib/ReactDOM.js
27 |
28 | .*/__mocks__/.*
29 | .*/__tests__/.*
30 |
31 | .*/commoner/test/source/widget/share.js
32 |
33 | # Ignore commoner tests
34 | .*/node_modules/commoner/test/.*
35 |
36 | # See https://github.com/facebook/flow/issues/442
37 | .*/react-tools/node_modules/commoner/lib/reader.js
38 |
39 | # Ignore jest
40 | .*/node_modules/jest-cli/.*
41 |
42 | # Ignore Website
43 | .*/website/.*
44 |
45 | # Ignore generators
46 | .*/local-cli/generator.*
47 |
48 | # Ignore BUCK generated folders
49 | .*\.buckd/
50 |
51 | .*/node_modules/is-my-json-valid/test/.*\.json
52 | .*/node_modules/iconv-lite/encodings/tables/.*\.json
53 | .*/node_modules/y18n/test/.*\.json
54 | .*/node_modules/spdx-license-ids/spdx-license-ids.json
55 | .*/node_modules/spdx-exceptions/index.json
56 | .*/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
57 | .*/node_modules/resolve/lib/core.json
58 | .*/node_modules/jsonparse/samplejson/.*\.json
59 | .*/node_modules/json5/test/.*\.json
60 | .*/node_modules/ua-parser-js/test/.*\.json
61 | .*/node_modules/builtin-modules/builtin-modules.json
62 | .*/node_modules/binary-extensions/binary-extensions.json
63 | .*/node_modules/url-regex/tlds.json
64 | .*/node_modules/joi/.*\.json
65 | .*/node_modules/isemail/.*\.json
66 | .*/node_modules/tr46/.*\.json
67 |
68 |
69 | [include]
70 |
71 | [libs]
72 | node_modules/react-native/Libraries/react-native/react-native-interface.js
73 | node_modules/react-native/flow
74 | flow/
75 |
76 | [options]
77 | module.system=haste
78 |
79 | esproposal.class_static_fields=enable
80 | esproposal.class_instance_fields=enable
81 |
82 | munge_underscores=true
83 |
84 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
85 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub'
86 |
87 | suppress_type=$FlowIssue
88 | suppress_type=$FlowFixMe
89 | suppress_type=$FixMe
90 |
91 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-3]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
92 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-3]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
93 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
94 |
95 | [version]
96 | 0.23.0
97 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IJ
26 | #
27 | .idea
28 | .gradle
29 | local.properties
30 |
31 | # node.js
32 | #
33 | node_modules/
34 | npm-debug.log
35 |
36 | # BUCK
37 | buck-out/
38 | \.buckd/
39 | android/app/libs
40 | android/keystores/debug.keystore
41 |
--------------------------------------------------------------------------------
/client/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/client/index.android.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | * https://github.com/facebook/react-native
4 | */
5 |
6 | import React, { Component } from 'react';
7 | import {
8 | AppRegistry,
9 | StyleSheet,
10 | Text,
11 | View
12 | } from 'react-native';
13 |
14 | class client extends Component {
15 | render() {
16 | return (
17 |
18 |
19 | Welcome to React Native!
20 |
21 |
22 | To get started, edit index.android.js
23 |
24 |
25 | Shake or press menu button for dev menu
26 |
27 |
28 | );
29 | }
30 | }
31 |
32 | const styles = StyleSheet.create({
33 | container: {
34 | flex: 1,
35 | justifyContent: 'center',
36 | alignItems: 'center',
37 | backgroundColor: '#F5FCFF',
38 | },
39 | welcome: {
40 | fontSize: 20,
41 | textAlign: 'center',
42 | margin: 10,
43 | },
44 | instructions: {
45 | textAlign: 'center',
46 | color: '#333333',
47 | marginBottom: 5,
48 | },
49 | });
50 |
51 | AppRegistry.registerComponent('client', () => client);
52 |
--------------------------------------------------------------------------------
/client/index.ios.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | * https://github.com/facebook/react-native
4 | * @flow
5 | */
6 |
7 | import React, { Component } from 'react';
8 | import {
9 | AppRegistry,
10 | StyleSheet,
11 | NavigatorIOS,
12 | Text,
13 | View
14 | } from 'react-native';
15 |
16 | var HomeView = require('./views/HomeView');
17 |
18 | class Client extends Component {
19 | render() {
20 | return (
21 |
27 | )
28 | }
29 | }
30 |
31 | var styles = StyleSheet.create({
32 | container: {
33 | flex: 1
34 | }
35 | });
36 |
37 | AppRegistry.registerComponent('client', () => Client);
38 |
39 | module.exports = Client;
40 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-native/local-cli/cli.js start"
7 | },
8 | "dependencies": {
9 | "babel-eslint": "^6.0.4",
10 | "eslint": "^2.10.1",
11 | "eslint-config-airbnb": "^9.0.1",
12 | "eslint-plugin-import": "^1.8.0",
13 | "eslint-plugin-jsx-a11y": "^1.2.0",
14 | "eslint-plugin-react": "^5.1.1",
15 | "react": "^0.14.8",
16 | "react-native": "^0.25.1",
17 | "tcomb-form-native": "^0.4.4"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/views/HomeView.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | AsyncStorage,
3 | Component,
4 | StyleSheet,
5 | TouchableHighlight,
6 | Text,
7 | View
8 | } from 'react-native'
9 |
10 | const LoginView = require('./LoginView')
11 | const RegisterView = require('./RegisterView')
12 | const ProtectedView = require('./ProtectedView')
13 |
14 | class HomeView extends Component {
15 | _handleRegisterView = () => {
16 | this.props.navigator.push({
17 | title: 'Register',
18 | component: RegisterView,
19 | backButtonTitle: 'Back'
20 | })
21 | }
22 | _handleLoginView = () => {
23 | this.props.navigator.push({
24 | title: 'Login',
25 | component: LoginView,
26 | backButtonTitle: 'Back'
27 | })
28 | }
29 | _handleProtectedView = () => {
30 | this.props.navigator.push({
31 | title: 'Protected Content',
32 | component: ProtectedView,
33 | backButtonTitle: 'Back'
34 | })
35 | }
36 | _handleLogOut = () => {
37 | AsyncStorage.removeItem('jwt');
38 | alert('You have been logged out.');
39 | }
40 | render() {
41 | return (
42 |
43 |
44 |
45 | Register
46 |
47 |
48 |
49 |
50 | Log In
51 |
52 |
53 |
54 |
55 | Log Out
56 |
57 |
58 |
59 |
60 | Protected Content
61 |
62 |
63 |
64 | )
65 | }
66 | }
67 |
68 | const styles = StyleSheet.create({
69 | container: {
70 | marginTop: 40,
71 | padding: 80,
72 | flex: 1,
73 | flexDirection: 'column'
74 | },
75 | button: {
76 | borderRadius: 4,
77 | padding: 20,
78 | textAlign: 'center',
79 | marginBottom: 20,
80 | color: '#fff'
81 | },
82 | greenButton: {
83 | backgroundColor: '#4CD964'
84 | },
85 | blueButton: {
86 | backgroundColor: '#34AADC'
87 | },
88 | redButton: {
89 | backgroundColor: '#FF3B30',
90 | color: '#fff'
91 | },
92 | greyButton: {
93 | backgroundColor: '#777',
94 | color: '#fff'
95 | }
96 | })
97 |
98 | module.exports = HomeView
99 |
--------------------------------------------------------------------------------
/client/views/LoginView.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | AsyncStorage,
3 | Component,
4 | ScrollView,
5 | StyleSheet,
6 | TouchableHighlight,
7 | Text
8 | } from 'react-native'
9 |
10 | const t = require('tcomb-form-native')
11 |
12 | const Form = t.form.Form
13 |
14 | const User = t.struct({
15 | email: t.String,
16 | password: t.String
17 | })
18 |
19 | const options = {
20 | fields: {
21 | email: {
22 | autoCapitalize: 'none',
23 | autoCorrect: false
24 | },
25 | password: {
26 | autoCapitalize: 'none',
27 | password: true,
28 | autoCorrect: false
29 | }
30 | }
31 | }
32 |
33 | class LoginView extends Component {
34 |
35 | constructor(props) {
36 | super(props)
37 | this.state = {
38 | value: {
39 | email: '',
40 | password: ''
41 | }
42 | }
43 | }
44 |
45 | componentWillUnmount() {
46 | this.setState = {
47 | value: {
48 | email: '',
49 | password: null
50 | }
51 | }
52 | }
53 |
54 | _onChange = (value) => {
55 | this.setState({
56 | value
57 | })
58 | }
59 | _handleAdd = () => {
60 | const value = this.refs.form.getValue();
61 | // If the form is valid...
62 | if (value) {
63 | const data = {
64 | username: value.email,
65 | password: value.password
66 | }
67 | // Serialize and post the data
68 | const json = JSON.stringify(data)
69 | fetch('http://localhost:3000/users/login', {
70 | method: 'POST',
71 | headers: {
72 | 'Content-Type': 'application/json',
73 | Accept: 'application/json'
74 | },
75 | body: json
76 | })
77 | .then((response) => response.json())
78 | .then((res) => {
79 | if (res.error) {
80 | alert(res.error)
81 | } else {
82 | AsyncStorage.setItem('jwt', res.token)
83 | alert(`Success! You may now access protected content.`)
84 | // Redirect to home screen
85 | this.props.navigator.pop()
86 | }
87 | })
88 | .catch(() => {
89 | alert('There was an error logging in.');
90 | })
91 | .done()
92 | } else {
93 | // Form validation error
94 | alert('Please fix the errors listed and try again.')
95 | }
96 | }
97 |
98 | render() {
99 | return (
100 |
101 |
108 |
109 | Log In
110 |
111 |
112 | )
113 | }
114 | };
115 |
116 | var styles = StyleSheet.create({
117 | container: {
118 | padding: 20,
119 | flex: 1,
120 | flexDirection: 'column'
121 | },
122 | button: {
123 | borderRadius: 4,
124 | padding: 20,
125 | textAlign: 'center',
126 | marginBottom: 20,
127 | color: '#fff'
128 | },
129 | greenButton: {
130 | backgroundColor: '#4CD964'
131 | },
132 | centering: {
133 | alignItems: 'center',
134 | justifyContent: 'center'
135 | }
136 | })
137 |
138 | module.exports = LoginView
139 |
--------------------------------------------------------------------------------
/client/views/ProtectedView.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | ActivityIndicatorIOS,
3 | AsyncStorage,
4 | Component,
5 | StyleSheet,
6 | Text,
7 | View
8 | } from 'react-native'
9 |
10 | class ProtectedView extends Component {
11 | constructor(props) {
12 | super(props)
13 | this.state = {
14 | showIndicator: false,
15 | secret: null
16 | }
17 | }
18 |
19 | componentWillMount() {
20 | this.setState({
21 | showIndicator: true
22 | }, this._fetchData)
23 | }
24 |
25 | _fetchData = () => {
26 | AsyncStorage.getItem('jwt', (err, token) => {
27 | fetch('http://localhost:3000/protected', {
28 | headers: {
29 | Accept: 'application/json',
30 | Authorization: `JWT ${token}`
31 | }
32 | })
33 | .then((response) => response.json())
34 | .then((json) => {
35 | this.setState({
36 | secret: json.secret,
37 | showIndicator: false
38 | })
39 | })
40 | .catch(() => {
41 | alert('There was an error fetching the secret info.')
42 | })
43 | .done()
44 | })
45 | }
46 |
47 | _renderIndicator = () => (
48 |
53 | )
54 |
55 | _renderSecret = () => (
56 |
57 | The secret code is {this.state.secret}
58 |
59 | )
60 |
61 | render() {
62 | return (
63 |
64 | {
65 | this.state.showIndicator
66 | ? this._renderIndicator()
67 | :
68 |
69 | {this.state.secret ? this._renderSecret() : You are not authorized!}
70 |
71 | }
72 |
73 | )
74 | }
75 | }
76 |
77 | var styles = StyleSheet.create({
78 | container: {
79 | marginTop: 40,
80 | padding: 80,
81 | flex: 1,
82 | flexDirection: 'column'
83 | },
84 | button: {
85 | borderRadius: 4,
86 | padding: 20,
87 | textAlign: 'center',
88 | marginBottom: 20,
89 | color: '#fff'
90 | },
91 | greenButton: {
92 | backgroundColor: '#4CD964'
93 | },
94 | blueButton: {
95 | backgroundColor: '#34AADC',
96 | },
97 | centering: {
98 | flex: 1,
99 | paddingTop: 28,
100 | justifyContent: 'center',
101 | alignItems: 'center'
102 | }
103 | })
104 |
105 | module.exports = ProtectedView
106 |
--------------------------------------------------------------------------------
/client/views/RegisterView.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component,
3 | ScrollView,
4 | StyleSheet,
5 | TouchableHighlight,
6 | Text
7 | } from 'react-native'
8 |
9 | const t = require('tcomb-form-native');
10 |
11 | const Form = t.form.Form
12 |
13 | const newUser = t.struct({
14 | email: t.String,
15 | password: t.String
16 | })
17 |
18 | const options = {
19 | fields: {
20 | email: {
21 | autoCapitalize: 'none',
22 | autoCorrect: false
23 | },
24 | password: {
25 | autoCapitalize: 'none',
26 | password: true,
27 | autoCorrect: false
28 | }
29 | }
30 | }
31 |
32 | class RegisterView extends Component {
33 |
34 | constructor(props) {
35 | super(props)
36 | this.state = {
37 | value: {
38 | email: '',
39 | password: ''
40 | }
41 | }
42 | }
43 |
44 | componentWillUnmount() {
45 | this.setState = {
46 | value: {
47 | email: '',
48 | password: null
49 | }
50 | }
51 | }
52 |
53 | _onChange = (value) => {
54 | this.setState({
55 | value
56 | })
57 | }
58 |
59 | _handleAdd = () => {
60 | const value = this.refs.form.getValue();
61 | // If the form is valid...
62 | if (value) {
63 | const data = {
64 | email: value.email,
65 | password: value.password,
66 | }
67 | // Serialize and post the data
68 | const json = JSON.stringify(data);
69 | fetch('http://localhost:3000/users/register', {
70 | method: 'POST',
71 | headers: {
72 | 'Content-Type': 'application/json',
73 | Accept: 'application/json'
74 | },
75 | body: json
76 | })
77 | .then((response) => response.json())
78 | .then(() => {
79 | alert('Success! You may now log in.');
80 | // Redirect to home screen
81 | this.props.navigator.pop();
82 | })
83 | .catch((error) => {
84 | alert('There was an error creating your account.');
85 | })
86 | .done()
87 | } else {
88 | // Form validation error
89 | alert('Please fix the errors listed and try again.')
90 | }
91 | }
92 |
93 | render() {
94 | return (
95 |
96 |
103 |
104 | Create account
105 |
106 |
107 | )
108 | }
109 | }
110 |
111 | const styles = StyleSheet.create({
112 | container: {
113 | padding: 20,
114 | flex: 1,
115 | flexDirection: 'column'
116 | },
117 | button: {
118 | borderRadius: 4,
119 | padding: 20,
120 | textAlign: 'center',
121 | marginBottom: 20,
122 | color: '#fff'
123 | },
124 | greenButton: {
125 | backgroundColor: '#4CD964'
126 | },
127 | centering: {
128 | alignItems: 'center',
129 | justifyContent: 'center'
130 | }
131 | })
132 |
133 | module.exports = RegisterView
134 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('serve-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 | var mongoose = require('mongoose');
8 | var passport = require('passport');
9 | var LocalStrategy = require('passport-local').Strategy;
10 | var JwtStrategy = require('passport-jwt').Strategy;
11 | var ExtractJwt = require('passport-jwt').ExtractJwt;
12 | var routes = require('./routes/index');
13 | var users = require('./routes/users');
14 | var User = require('./models/user');
15 |
16 | var app = express();
17 |
18 | // JWT configration
19 | var options = {}
20 | options.jwtFromRequest = ExtractJwt.fromAuthHeader();
21 | options.secretOrKey = '7x0jhxt"9(thpX6'
22 |
23 | app.use(passport.initialize());
24 |
25 | // view engine setup
26 | app.set('views', path.join(__dirname, 'views'));
27 | app.set('view engine', 'jade');
28 |
29 | // uncomment after placing your favicon in /public
30 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
31 | app.use(logger('dev'));
32 | app.use(bodyParser.json());
33 | app.use(bodyParser.urlencoded({
34 | extended: false
35 | }));
36 | app.use(cookieParser());
37 | app.use(express.static(path.join(__dirname, 'public')));
38 |
39 | // Configure Passport to use local strategy for initial authentication.
40 | passport.use('local', new LocalStrategy(User.authenticate()));
41 |
42 | // Configure Passport to use JWT strategy to look up Users.
43 | passport.use('jwt', new JwtStrategy(options, function(jwt_payload, done) {
44 | User.findOne({
45 | _id: jwt_payload.id
46 | }, function(err, user) {
47 | if (err) {
48 | return done(err, false);
49 | }
50 | if (user) {
51 | done(null, user);
52 | } else {
53 | done(null, false);
54 | }
55 | })
56 | }))
57 |
58 | app.use('/', routes);
59 | app.use('/users', users);
60 |
61 | // connect to database
62 | var db = process.env.MONGO_URL || 'localhost/react-native-jwt'
63 | mongoose.connect(db);
64 | mongoose.connection.on('error', function() {
65 | console.info('Error: Could not connect to MongoDB. Did you forget to run `mongod`?')
66 | });
67 |
68 | // catch 404 and forward to error handler
69 | app.use(function(req, res, next) {
70 | var err = new Error('Not Found');
71 | err.status = 404;
72 | next(err);
73 | });
74 |
75 | // error handlers
76 |
77 | // development error handler
78 | // will print stacktrace
79 | // app.use(function(err, req, res, next) {
80 | // res.status(err.status || 500);
81 | // res.render('error', {
82 | // message: err.message,
83 | // error: err
84 | // });
85 | // });
86 | // }
87 |
88 | // production error handler
89 | // no stacktraces leaked to user
90 | app.use(function(err, req, res, next) {
91 | res.status(err.status || 500);
92 | res.render('error', {
93 | message: err.message,
94 | error: {}
95 | });
96 | });
97 |
98 |
99 | module.exports = app;
100 |
--------------------------------------------------------------------------------
/server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('passport-local-express4:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3000');
16 | app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app);
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | server.listen(port);
29 | server.on('error', onError);
30 | server.on('listening', onListening);
31 |
32 | /**
33 | * Normalize a port into a number, string, or false.
34 | */
35 |
36 | function normalizePort(val) {
37 | var port = parseInt(val, 10);
38 |
39 | if (isNaN(port)) {
40 | // named pipe
41 | return val;
42 | }
43 |
44 | if (port >= 0) {
45 | // port number
46 | return port;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | /**
53 | * Event listener for HTTP server "error" event.
54 | */
55 |
56 | function onError(error) {
57 | if (error.syscall !== 'listen') {
58 | throw error;
59 | }
60 |
61 | var bind = typeof port === 'string'
62 | ? 'Pipe ' + port
63 | : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string'
87 | ? 'pipe ' + addr
88 | : 'port ' + addr.port;
89 | debug('Listening on ' + bind);
90 | }
91 |
--------------------------------------------------------------------------------
/server/models/user.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var Schema = mongoose.Schema;
3 | var passportLocalMongoose = require('passport-local-mongoose');
4 |
5 | var User = new Schema({});
6 |
7 | // See passport-local-mongoose docs for schema customization options
8 | // https://github.com/saintedlama/passport-local-mongoose#options
9 | User.plugin(passportLocalMongoose, {
10 | usernameField: 'email',
11 | usernameUnique: true
12 | });
13 |
14 | module.exports = mongoose.model('User', User);
15 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-jwt",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "body-parser": "~1.13.2",
10 | "cookie-parser": "~1.3.5",
11 | "debug": "~2.2.0",
12 | "express": "~4.13.1",
13 | "jade": "~1.11.0",
14 | "jsonwebtoken": "^6.2.0",
15 | "mongoose": "^4.4.16",
16 | "morgan": "~1.6.1",
17 | "passport": "^0.3.2",
18 | "passport-jwt": "^2.0.0",
19 | "passport-local": "^1.0.0",
20 | "passport-local-mongoose": "^4.0.0",
21 | "serve-favicon": "~2.3.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/server/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | a {
7 | color: #00B7FF;
8 | }
9 |
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | var passport = require('passport');
4 |
5 | /* GET home page. */
6 | router.get('/protected', function (req, res, next) {
7 | console.log(req.headers);
8 | passport.authenticate('jwt', function (err, user, info) {
9 | if (err) {
10 | return next(err);
11 | }
12 | if (!user) {
13 | return res.status(401).json({ error: 'Invalid credentials.' });
14 | }
15 | if (user) {
16 | return res
17 | .status(200)
18 | .json({ secret: '123' });
19 | }
20 | })(req, res, next);
21 | });
22 |
23 | module.exports = router;
24 |
--------------------------------------------------------------------------------
/server/routes/users.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 | var passport = require('passport');
4 | var jwt = require('jsonwebtoken');
5 | var User = require('../models/user');
6 |
7 | var secret = '7x0jhxt"9(thpX6';
8 |
9 | router.post('/login', function (req, res, next) {
10 | passport.authenticate('local', function (err, user, info) {
11 | if (err) {
12 | return next(err);
13 | }
14 | if (!user) {
15 | return res.status(401).json({ error: 'Invalid credentials.' });
16 | }
17 | if (user) {
18 | var token = jwt.sign({ id: user._id, email: user.email }, secret);
19 | return res
20 | .status(200)
21 | .json({ token });
22 | }
23 | })(req, res, next);
24 | });
25 |
26 | router.post('/register', function (req, res) {
27 | User.register(new User({ email: req.body.email }), req.body.password, function (err, user) {
28 | if (err) {
29 | return res.status(400).send({ error: 'Email address in use.' })
30 | }
31 | res.status(200).send(user);
32 | });
33 | });
34 |
35 | module.exports = router;
36 |
--------------------------------------------------------------------------------
/server/views/error.jade:
--------------------------------------------------------------------------------
1 | extends layout
2 |
3 | block content
4 | h1= message
5 | h2= error.status
6 | pre #{error.stack}
7 |
--------------------------------------------------------------------------------
/server/views/index.jade:
--------------------------------------------------------------------------------
1 | extends layout
2 |
3 | block content
4 | h1= title
5 | p Welcome to #{title}
6 |
--------------------------------------------------------------------------------
/server/views/layout.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | title= title
5 | link(rel='stylesheet', href='/stylesheets/style.css')
6 | body
7 | block content
8 |
--------------------------------------------------------------------------------