├── Facebook.js ├── Google.js ├── Navbar.js ├── Server.js ├── Style.js ├── cli.js ├── icons ├── ic_action_more.png ├── ic_action_more@2x.png ├── ic_action_more@3x.png ├── ic_action_refresh.png ├── ic_action_refresh@2x.png ├── ic_action_refresh@3x.png ├── ic_action_search.png ├── ic_action_search@2x.png └── ic_action_search@3x.png ├── index.js ├── package.json ├── preview.gif └── readme.md /Facebook.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | Text, 7 | View, 8 | WebView, 9 | ToastAndroid, 10 | AsyncStorage, 11 | Image, 12 | TouchableHighlight 13 | } from 'react-native'; 14 | 15 | import api, {key, facebook} from './Server'; 16 | import stylesheet from './Style'; 17 | 18 | export default class extends Component { 19 | constructor(props) { 20 | super(props); 21 | 22 | this.state = { 23 | code: undefined, 24 | token: undefined, 25 | access: undefined, 26 | response: undefined, 27 | loading: false, 28 | }; 29 | } 30 | 31 | render() { 32 | if (this.state.response) return this.renderResponse(); 33 | if (this.state.access) return this.fetchProfile(); 34 | if (this.state.token) return this.fetchAccess(); 35 | if (this.state.code) return this.fetchToken(); 36 | 37 | return this.renderScene(); 38 | } 39 | 40 | renderScene() { 41 | return ( 42 | 49 | ); 50 | } 51 | 52 | renderResponse() { 53 | let profile = JSON.parse(this.state.response); 54 | 55 | return ( 56 | 57 | 58 | {profile.name} 59 | this.onLogout()}> 63 | {this.state.loading ? `Please Wait . . .` : `Logout`} 64 | 65 | 66 | ); 67 | } 68 | 69 | fetchToken() { 70 | if (this.state.token) return null; 71 | 72 | api.facebook.token(this.state.code) 73 | .then((response) => { 74 | if (!response.ok) throw Error(response.statusText || response._bodyText); 75 | return response.json() 76 | }) 77 | .then((responseData) => { 78 | if (responseData && responseData.access_token) { 79 | this.setState({ 80 | token: responseData.access_token 81 | }); 82 | } 83 | }) 84 | .catch((error) => { 85 | this.onError(error); 86 | }) 87 | .done(); 88 | 89 | return null; 90 | } 91 | 92 | fetchAccess() { 93 | if (this.state.access) return null; 94 | 95 | api.facebook.access(this.state.token) 96 | .then((response) => { 97 | if ((/access_token=/g).test(response._bodyText)) { 98 | this.setState({ 99 | access: String(response._bodyText.split('&')[0]).replace('access_token=','') 100 | }); 101 | } 102 | }) 103 | .catch((error) => { 104 | this.onError(error); 105 | }) 106 | .done(); 107 | 108 | return null; 109 | } 110 | 111 | fetchProfile() { 112 | if (this.state.response) return null; 113 | 114 | api.facebook.profile(this.state.access) 115 | .then((response) => { 116 | if (!response.ok) throw Error(response.statusText || response._bodyText); 117 | return response.json() 118 | }) 119 | .then((responseData) => { 120 | this.setState({ 121 | response: JSON.stringify(responseData) 122 | }); 123 | this.saveResponse().done(); 124 | }) 125 | .catch((error) => { 126 | this.onError(error); 127 | }) 128 | .done(); 129 | 130 | return null; 131 | } 132 | 133 | onNavigationStateChange(navState) { 134 | if ((/code=/g).test(String(navState.url))) { 135 | this.setState({ 136 | code: String(navState.url).replace(`${facebook.redirect_uri}?code=`,'') 137 | }); 138 | } 139 | } 140 | 141 | onLogout() { 142 | if (this.state.loading) { 143 | ToastAndroid.show(`Please Wait . . .`, ToastAndroid.SHORT); 144 | return null; 145 | } 146 | 147 | this.setState({loading: true}); 148 | 149 | api.facebook.logout(this.state.access) 150 | .then((response) => { 151 | if (!response.ok) throw Error(response.statusText || response._bodyText); 152 | 153 | this.setState({ 154 | code: undefined, 155 | token: undefined, 156 | access: undefined, 157 | response: undefined 158 | }); 159 | 160 | this.removeResponse().done(); 161 | }) 162 | .catch((error) => { 163 | this.onError(error); 164 | }) 165 | .done(() => { 166 | this.setState({loading: false}); 167 | }); 168 | 169 | return null; 170 | } 171 | 172 | onError(argument) { 173 | console.log(argument); 174 | ToastAndroid.show(String(argument).replace('Error: ',''), ToastAndroid.LONG); 175 | } 176 | 177 | async saveResponse() { 178 | try { 179 | await AsyncStorage.setItem(key, JSON.stringify(this.state.response)); 180 | } catch (error) { 181 | this.onError(error); 182 | } 183 | } 184 | 185 | async removeResponse() { 186 | try { 187 | await AsyncStorage.removeItem(key); 188 | ToastAndroid.show(`Logout successfully!`, ToastAndroid.SHORT); 189 | } catch (error) { 190 | this.onError(error); 191 | } 192 | } 193 | } 194 | 195 | const styles = StyleSheet.create(stylesheet); 196 | -------------------------------------------------------------------------------- /Google.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | Text, 7 | View, 8 | WebView, 9 | ToastAndroid, 10 | TouchableHighlight, 11 | Image, 12 | AsyncStorage 13 | } from 'react-native'; 14 | 15 | import api, {key, google} from './Server'; 16 | import stylesheet from './Style'; 17 | 18 | export default class extends Component { 19 | constructor(props) { 20 | super(props); 21 | 22 | this.state = { 23 | code: undefined, 24 | token: undefined, 25 | response: undefined, 26 | loading: false 27 | }; 28 | } 29 | 30 | render() { 31 | if (this.state.response) return this.renderResponse(); 32 | if (this.state.token) return this.fetchProfile(); 33 | if (this.state.code) return this.fetchToken(); 34 | 35 | return this.renderScene(); 36 | } 37 | 38 | renderScene() { 39 | return ( 40 | 47 | ); 48 | } 49 | 50 | renderResponse() { 51 | let profile = JSON.parse(this.state.response); 52 | 53 | return ( 54 | 55 | 56 | {profile.name} 57 | this.onLogout()}> 61 | {this.state.loading ? `Please Wait . . .` : `Logout`} 62 | 63 | 64 | ); 65 | } 66 | 67 | fetchToken() { 68 | if (this.state.token) return null; 69 | 70 | api.google.token(this.state.code) 71 | .then((response) => { 72 | if (!response.ok) throw Error(response.statusText || response._bodyText); 73 | return response.json() 74 | }) 75 | .then((responseData) => { 76 | if (responseData && responseData.access_token) { 77 | this.setState({ 78 | token: responseData.access_token 79 | }); 80 | } 81 | }) 82 | .catch((error) => { 83 | this.onError(error); 84 | }) 85 | .done(); 86 | 87 | return null; 88 | } 89 | 90 | fetchProfile() { 91 | if (this.state.response) return null; 92 | 93 | api.google.profile(this.state.token) 94 | .then((response) => { 95 | if (!response.ok) throw Error(response.statusText || response._bodyText); 96 | return response.json() 97 | }) 98 | .then((responseData) => { 99 | this.setState({ 100 | response: JSON.stringify(responseData) 101 | }); 102 | this.saveResponse().done(); 103 | }) 104 | .catch((error) => { 105 | this.onError(error); 106 | }) 107 | .done(); 108 | 109 | return null; 110 | } 111 | 112 | onNavigationStateChange(navState) { 113 | if ((/code=/g).test(String(navState.url))) { 114 | this.setState({ 115 | code: String(navState.url).replace(`${google.redirect_uri}?code=`,'') 116 | }); 117 | } 118 | } 119 | 120 | onLogout() { 121 | if (this.state.loading) { 122 | ToastAndroid.show(`Please Wait . . .`, ToastAndroid.SHORT); 123 | return null; 124 | } 125 | 126 | this.setState({loading: true}); 127 | 128 | api.google.logout(this.state.token) 129 | .then((response) => { 130 | if (!response.ok) throw Error(response.statusText || response._bodyText); 131 | 132 | this.setState({ 133 | code: undefined, 134 | token: undefined, 135 | response: undefined 136 | }); 137 | 138 | this.removeResponse().done(); 139 | }) 140 | .catch((error) => { 141 | this.onError(error); 142 | }) 143 | .done(() => { 144 | this.setState({loading: false}); 145 | }); 146 | 147 | return null; 148 | } 149 | 150 | onError(argument) { 151 | console.log(argument); 152 | ToastAndroid.show(String(argument).replace('Error: ',''), ToastAndroid.LONG); 153 | } 154 | 155 | async saveResponse() { 156 | try { 157 | await AsyncStorage.setItem(key, JSON.stringify(this.state.response)); 158 | } catch (error) { 159 | this.onError(error); 160 | } 161 | } 162 | 163 | async removeResponse() { 164 | try { 165 | await AsyncStorage.removeItem(key); 166 | ToastAndroid.show(`Logout successfully!`, ToastAndroid.SHORT); 167 | } catch (error) { 168 | this.onError(error); 169 | } 170 | } 171 | } 172 | 173 | const styles = StyleSheet.create(stylesheet); 174 | -------------------------------------------------------------------------------- /Navbar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { 4 | Component, 5 | StyleSheet, 6 | ToastAndroid, 7 | View, 8 | Text, 9 | TextInput, 10 | PropTypes 11 | } from 'react-native'; 12 | 13 | import ToolbarAndroid from 'ToolbarAndroid'; 14 | 15 | export default class extends Component { 16 | static propTypes = { 17 | onSearch: PropTypes.func, 18 | onRefresh: PropTypes.func 19 | }; 20 | 21 | constructor(props) { 22 | super(props); 23 | 24 | this.state = { 25 | search: false, 26 | query: undefined 27 | }; 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 | 38 | {this.state.search ? this.renderSearch() : this.renderTitle()} 39 | 40 | 41 | ); 42 | } 43 | 44 | renderTitle() { 45 | return ( 46 | 47 | {this.props.title} 48 | 49 | ); 50 | } 51 | 52 | renderSearch() { 53 | return ( 54 | 55 | this.state.query = text} 62 | onSubmitEditing={() => this.onSearch()} 63 | /> 64 | 65 | ); 66 | } 67 | 68 | onActionSelected(position) { 69 | switch (position) { 70 | case 0: this.onSearch(); break; 71 | case 1: this.onRefresh(); break; 72 | default: ToastAndroid.show(`${actions[position].title} selected.`, ToastAndroid.SHORT); 73 | } 74 | } 75 | 76 | onSearch() { 77 | this.props.onSearch && this.props.onSearch(); 78 | 79 | if (this.state.query) ToastAndroid.show(`${this.state.query} not found`, ToastAndroid.SHORT); 80 | this.setState({search: !this.state.search, query: undefined}); 81 | } 82 | 83 | onRefresh() { 84 | this.props.onRefresh && this.props.onRefresh(); 85 | } 86 | } 87 | 88 | const icons = { 89 | more: require('./icons/ic_action_more.png'), 90 | search: require('./icons/ic_action_search.png'), 91 | refresh: require('./icons/ic_action_refresh.png'), 92 | }; 93 | 94 | const actions = [ 95 | {title: 'Search', icon: icons.search, show: 'always'}, 96 | {title: 'Refresh', icon: icons.refresh, show: 'ifRoom'}, 97 | {title: 'Single Sign On'}, 98 | {title: 'Notifications'}, 99 | ]; 100 | 101 | const styles = StyleSheet.create({ 102 | container: { 103 | flex: 1, 104 | alignItems: 'stretch', 105 | }, 106 | toolbar: { 107 | height: 60, 108 | backgroundColor: '#00796B' 109 | }, 110 | titleContainer: { 111 | flex: 1, 112 | justifyContent: 'center', 113 | backgroundColor: 'transparent', 114 | alignItems: 'center', 115 | }, 116 | title: { 117 | alignSelf: 'flex-start', 118 | backgroundColor: 'transparent', 119 | fontSize: 20, 120 | color: '#ffffff' 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /Server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export const key = '@lussatech:session'; 4 | 5 | export const facebook = { 6 | client_id: '1505435769762540', 7 | client_secret: '210bdcf50efa27bc5bfc93b81a8b903d', 8 | oauth_dialog: 'https://www.facebook.com/dialog/oauth', 9 | redirect_uri: 'https://www.facebook.com/connect/login_success.html', 10 | oauth_token: 'https://graph.facebook.com/v2.3/oauth/access_token', 11 | oauth_access: 'https://graph.facebook.com/oauth/access_token', 12 | oauth_profile: 'https://graph.facebook.com/v2.5/me', 13 | oauth_logout: 'https://www.facebook.com/logout.php', 14 | }; 15 | 16 | export const google = { 17 | client_id: '119869562795-rtsscilu9cb895r35dkokrvi6no9hvcd.apps.googleusercontent.com', 18 | client_secret: '87bg-4ElD3iUHI9kwR_Z1w2v', 19 | oauth_dialog: 'https://accounts.google.com/o/oauth2/v2/auth', 20 | redirect_uri: 'https://www.facebook.com/connect/login_success.html', 21 | oauth_token: 'https://www.googleapis.com/oauth2/v4/token', 22 | oauth_profile: 'https://www.googleapis.com/oauth2/v3/userinfo', 23 | oauth_logout: 'https://accounts.google.com/o/oauth2/revoke', 24 | }; 25 | 26 | export default { 27 | facebook: { 28 | token: function (code) { 29 | let url = `${facebook.oauth_token}?client_id=${facebook.client_id}&client_secret=${facebook.client_secret}&redirect_uri=${facebook.redirect_uri}&code=${code}`, 30 | opt = { 31 | method: 'get' 32 | }; 33 | 34 | return fetch(url, opt); 35 | }, 36 | access: function (token) { 37 | let url = `${facebook.oauth_access}?grant_type=fb_exchange_token&client_id=${facebook.client_id}&client_secret=${facebook.client_secret}&fb_exchange_token=${token}`, 38 | opt = { 39 | method: 'get' 40 | }; 41 | 42 | return fetch(url, opt); 43 | }, 44 | profile: function (access) { 45 | let url = `${facebook.oauth_profile}?fields=id,name,email,about,age_range,picture&access_token=${access}`, 46 | opt = { 47 | method: 'get' 48 | }; 49 | 50 | return fetch(url, opt); 51 | }, 52 | logout: function (access) { 53 | let url = `${facebook.oauth_logout}?next=${facebook.redirect_uri}&access_token=${access}`, 54 | opt = { 55 | method: 'get' 56 | }; 57 | 58 | return fetch(url, opt); 59 | }, 60 | }, 61 | google: { 62 | token: function (code) { 63 | let url = `${google.oauth_token}?grant_type=authorization_code&client_id=${google.client_id}&client_secret=${google.client_secret}&redirect_uri=${google.redirect_uri}&code=${code}`, 64 | opt = { 65 | method: 'post', 66 | headers: { 67 | 'Content-Type': 'application/x-www-form-urlencoded' 68 | } 69 | }; 70 | 71 | return fetch(url, opt); 72 | }, 73 | profile: function (token) { 74 | let url = `${google.oauth_profile}?alt=json&access_token=${token}`, 75 | opt = { 76 | method: 'get' 77 | }; 78 | 79 | return fetch(url, opt); 80 | }, 81 | logout: function (token) { 82 | let url = `${google.oauth_logout}?token=${token}`, 83 | opt = { 84 | method: 'get' 85 | }; 86 | 87 | return fetch(url, opt); 88 | }, 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /Style.js: -------------------------------------------------------------------------------- 1 | export default { 2 | container: { 3 | flex: 1, 4 | justifyContent: 'center', 5 | alignItems: 'center', 6 | }, 7 | welcome: { 8 | fontSize: 17, 9 | textAlign: 'center', 10 | margin: 10, 11 | }, 12 | instructions: { 13 | textAlign: 'center', 14 | color: '#333333', 15 | marginBottom: 5, 16 | }, 17 | button: { 18 | padding: 15, 19 | margin: 20, 20 | justifyContent: 'center', 21 | alignSelf: 'stretch' 22 | }, 23 | buttonActive: { 24 | backgroundColor: '#FFB300', 25 | }, 26 | buttonDisabled: { 27 | backgroundColor: '#ffc300', 28 | }, 29 | buttonText: { 30 | fontSize: 15, 31 | color: 'white', 32 | alignSelf: 'center' 33 | }, 34 | image: { 35 | width: 200, 36 | height: 200 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs') 4 | , path = require('path') 5 | , source = path.resolve('node_modules', 'react-native-sso-authentication') 6 | , target = path.resolve(process.cwd(), 'lib') 7 | , ignore = ['cli.js', 'package.json', 'readme.md', 'preview.gif']; 8 | 9 | function run() { 10 | try { 11 | var packages = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf8')) || undefined; 12 | } catch (e) { 13 | console.error('package.json couldn\'t be found, maybe `%s` is not the root of project directory', process.cwd()); 14 | process.exit(1); 15 | } finally { 16 | var react = Object.keys(packages.dependencies).map(function (val) { 17 | if (val === 'react-native') return true; 18 | }); 19 | 20 | if (react[0]) { 21 | writing(source, target); 22 | } else { 23 | console.error('react-native dependencies couldn\'t be found, maybe `%s` is not the root of react-native project directory', process.cwd()); 24 | process.exit(1); 25 | } 26 | } 27 | } 28 | 29 | function writing(source, target) { 30 | if (!fs.existsSync(target)) { 31 | fs.mkdirSync(target); 32 | } 33 | 34 | copyFolderSync(source, target); 35 | } 36 | 37 | function copyFileSync(source, target) { 38 | var targetFile = target; 39 | 40 | if (ignore.indexOf(path.basename(source)) > -1) return; 41 | 42 | if(fs.existsSync(target)) { 43 | if(fs.lstatSync(target).isDirectory()) { 44 | targetFile = path.join(target, path.basename(source)); 45 | } 46 | } 47 | 48 | fs.writeFileSync(targetFile, fs.readFileSync(source)); 49 | } 50 | 51 | function copyFolderSync(source, target) { 52 | var files = []; 53 | var targetFolder = path.join(target, path.basename(source)); 54 | 55 | if (!fs.existsSync(targetFolder)) { 56 | fs.mkdirSync(targetFolder); 57 | } 58 | 59 | if(fs.lstatSync(source).isDirectory()) { 60 | files = fs.readdirSync(source); 61 | files.forEach(function (file) { 62 | var curSource = path.join(source, file); 63 | 64 | if (fs.lstatSync(curSource).isDirectory()) { 65 | copyFolderSync(curSource, targetFolder); 66 | } else { 67 | copyFileSync(curSource, targetFolder); 68 | } 69 | }); 70 | } 71 | } 72 | 73 | module.exports = { 74 | run: run 75 | }; 76 | -------------------------------------------------------------------------------- /icons/ic_action_more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_more.png -------------------------------------------------------------------------------- /icons/ic_action_more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_more@2x.png -------------------------------------------------------------------------------- /icons/ic_action_more@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_more@3x.png -------------------------------------------------------------------------------- /icons/ic_action_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_refresh.png -------------------------------------------------------------------------------- /icons/ic_action_refresh@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_refresh@2x.png -------------------------------------------------------------------------------- /icons/ic_action_refresh@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_refresh@3x.png -------------------------------------------------------------------------------- /icons/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_search.png -------------------------------------------------------------------------------- /icons/ic_action_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_search@2x.png -------------------------------------------------------------------------------- /icons/ic_action_search@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/icons/ic_action_search@3x.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { 4 | AppRegistry, 5 | Component, 6 | StyleSheet, 7 | Text, 8 | View, 9 | TouchableHighlight, 10 | ScrollView, 11 | ToastAndroid 12 | } from 'react-native'; 13 | 14 | import ToolbarAndroid from 'ToolbarAndroid'; 15 | 16 | import Facebook from './Facebook'; 17 | import Google from './Google'; 18 | import Navbar from './Navbar'; 19 | import Server, {key as Key, facebook as OAuthFacebook, google as OAuthGoogle} from './Server'; 20 | import Style from './Style'; 21 | 22 | export {Facebook, Google, Server, Key, OAuthFacebook, OAuthGoogle, Style}; 23 | 24 | export default class extends Component { 25 | constructor(props) { 26 | super(props); 27 | 28 | this.state = { 29 | scene: undefined 30 | }; 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | 37 | this.setState({scene: undefined})} /> 38 | 39 | 40 | {this.state.scene === 'facebook' ? this.renderFacebook() : this.state.scene === 'google' ? this.renderGoogle() : this.renderScene()} 41 | 42 | 43 | ); 44 | } 45 | 46 | renderScene() { 47 | return ( 48 | 49 | this.setState({scene: 'facebook'})}> 53 | {`Sign In with Facebook`} 54 | 55 | this.setState({scene: 'google'})}> 59 | {`Sign In with Google`} 60 | 61 | 62 | ); 63 | } 64 | 65 | renderFacebook() { 66 | return ; 67 | } 68 | 69 | renderGoogle() { 70 | return ; 71 | } 72 | } 73 | 74 | const styles = StyleSheet.create({ 75 | welcome: { 76 | fontSize: 20, 77 | textAlign: 'center', 78 | margin: 10, 79 | }, 80 | instructions: { 81 | textAlign: 'center', 82 | color: '#333333', 83 | marginBottom: 5, 84 | }, 85 | button: { 86 | height: 50, 87 | alignSelf: 'stretch', 88 | justifyContent: 'center', 89 | margin: 20, 90 | }, 91 | buttonText: { 92 | fontSize: 18, 93 | color: 'white', 94 | alignSelf: 'center' 95 | }, 96 | buttonFacebook: { 97 | backgroundColor: '#3A5795', 98 | }, 99 | buttonGoogle: { 100 | backgroundColor: '#EA4335', 101 | }, 102 | toolbarContainer: { 103 | flex: 1, 104 | alignItems: 'stretch', 105 | }, 106 | toolbar: { 107 | height: 100, 108 | backgroundColor: '#00796B', 109 | }, 110 | titleContainer: { 111 | flex: 1, 112 | justifyContent: 'center', 113 | backgroundColor: 'transparent', 114 | alignItems: 'center', 115 | marginRight: 34 116 | }, 117 | title: { 118 | alignSelf: 'center', 119 | backgroundColor: 'transparent', 120 | fontWeight: 'bold', 121 | fontSize: 30, 122 | color: '#ffffff' 123 | } 124 | }); 125 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-sso-authentication", 3 | "version": "1.0.3", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "Lussa Teknologi", 9 | "license": "ISC", 10 | "description": "a native sso authentication for react native", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/lussatech/react-native-sso-authentication.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/lussatech/react-native-sso-authentication/issues" 17 | }, 18 | "homepage": "https://github.com/lussatech/react-native-sso-authentication" 19 | } 20 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/ffac046590b4261d869479e73b72e3665257b016/preview.gif -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![react-native-sso-authentication](https://raw.githubusercontent.com/lussatech/react-native-sso-authentication/master/preview.gif) 2 | 3 | ### Installation 4 | npm i react-native-sso-authentication 5 | 6 | ### Generate Files 7 | Before generate library files to your react-native-project, make sure that `lussatech-cli` is installed globally in your machine, otherwise use this command to install it: 8 | 9 | npm i lussatech-cli -g 10 | 11 | If `lussatech-cli` have been installed, change directory to your react-native-project and run this command: 12 | 13 | lussatech generate react-native-sso-authentication 14 | 15 | then the library files will be added automatically inside your react-native-project, e.g. 16 | 17 | react-native-project 18 | |_ ... 19 | |_ lib 20 | |_ react-native-sso-authentication 21 | |_ ... 22 | |_ index.js 23 | |_ ... 24 | 25 | ### Usage 26 | ```javascript 27 | ... 28 | import SSOAuth, { // sample app 29 | /* available components */ 30 | Navbar, // sample navigation bar 31 | Facebook, // sample facebook view 32 | Google, // sample google view 33 | /* available constants */ 34 | Server, // sample api end-point 35 | Key, // sample key for asynstorage 36 | OAuthFacebook, // sample oauth facebook setting 37 | OAuthGoogle, // sample oauth google setting 38 | Style // sample styles 39 | } from './lib/react-native-sso-authentication'; 40 | 41 | class Name extends Component { 42 | render() { 43 | return ( 44 | // sample calling component 45 | ); 46 | } 47 | } 48 | ... 49 | ``` 50 | 51 | ###### Manage OAuth credentials 52 | To manage oauth credentials, update `Server.js` based on your oauth credentials, e.g. 53 | 54 | ```javascript 55 | # lib/react-native-sso-authentication/Server.js 56 | 57 | ... 58 | export const key = '@lussatech:session'; // key for asynstorage 59 | export const facebook = { 60 | client_id: '', // The id of your facebook app (Facebook App ID) 61 | client_secret: '', // The secret of your facebook app (Facebook App Secret) 62 | oauth_dialog: '', // The uri to display facebook login dialog 63 | redirect_uri: '', // The uri to capture response (code) from login dialog 64 | oauth_token: '', // The uri to exchange response (code) for an accessing token (short-lived-token) 65 | oauth_access: '', // The uri to exchange short-lived-token for long-lived-token 66 | oauth_profile: '', // The uri to get logged in user profile 67 | oauth_logout: '', // The uri to logged out user 68 | }; 69 | export const google = { 70 | client_id: '', // The id of your google app (Google App ID) 71 | client_secret: '', // The secret of your google app (Google App Secret) 72 | oauth_dialog: '', // The uri to display google login dialog 73 | redirect_uri: '', // The uri to capture response (code) from login dialog 74 | oauth_token: '', // The uri to exchange response (code) for an accessing token 75 | oauth_profile: '', // The uri to get logged in user profile 76 | oauth_logout: '', // The uri to logged out user 77 | }; 78 | ... 79 | ``` 80 | 81 | ###### Customize navigation bar 82 | To customize navigation bar, update `Navbar.js` based on your need, e.g. 83 | 84 | ```javascript 85 | # lib/react-native-sso-authentication/Navbar.js 86 | 87 | ... 88 | export default class extends Component { 89 | /* to validate props value */ 90 | static propTypes = { 91 | onRefresh: PropTypes.func, 92 | ... 93 | }; 94 | ... 95 | 96 | /* when a menu is selected */ 97 | onActionSelected(position) { 98 | switch (position) { 99 | case 0: this.onSearch(); break; 100 | case 1: this.onRefresh(); break; 101 | ... 102 | default: ToastAndroid.show(`${actions[position].title} selected.`, ToastAndroid.SHORT); 103 | } 104 | } 105 | ... 106 | 107 | /* when selected menu is `Refresh` */ 108 | onRefresh() { 109 | /* calling onRefresh props action if available */ 110 | this.props.onRefresh && this.props.onRefresh(); 111 | } 112 | ... 113 | } 114 | 115 | /* list of menu */ 116 | const actions = [ 117 | {title: 'Search', icon: icons.search, show: 'always'}, 118 | {title: 'Refresh', icon: icons.refresh, show: 'ifRoom'}, 119 | ... 120 | ]; 121 | ... 122 | ``` 123 | 124 | #### Customize views 125 | To customize views, update `Facebook.js` and `Google.js` based on your need, e.g. 126 | 127 | ```javascript 128 | # lib/react-native-sso-authentication/Google.js 129 | 130 | ... 131 | render() { 132 | if (this.state.response) return this.renderResponse(); 133 | if (this.state.token) return this.fetchProfile(); 134 | if (this.state.code) return this.fetchToken(); 135 | 136 | return this.renderScene(); 137 | } 138 | 139 | renderScene() { 140 | return ( 141 | 148 | ); 149 | } 150 | 151 | renderResponse() { 152 | let profile = JSON.parse(this.state.response); 153 | 154 | return ( 155 | 156 | 157 | {profile.name} 158 | this.onLogout()}> 162 | {this.state.loading ? `Please Wait . . .` : `Logout`} 163 | 164 | 165 | ); 166 | } 167 | ... 168 | ``` 169 | --------------------------------------------------------------------------------