Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google service account authentication from react native

I'd like to use service account authentication within my react native app, in order to let people update a spreadsheet without the need for them to log into their google account.

I'm pretty lost on how to do it, since all frontend code I see is related to traditional oauth2 login.

Any idea?

like image 972
xavier.seignard Avatar asked Jan 31 '17 02:01

xavier.seignard


2 Answers

I finally dropped the idea of service account and used client oauth.

I relied on the following React Native library : https://github.com/joonhocho/react-native-google-sign-in

Here is my authentication service (simplified, redux + redux thunk based):

// <project_folder>/src/services/auth.js
import GoogleSignIn from 'react-native-google-sign-in';
import { store } from '../store';
import auth from '../actions/auth';

export default () => {
    GoogleSignIn.configure({
        // https://developers.google.com/identity/protocols/googlescopes
        scopes: ['https://www.googleapis.com/auth/spreadsheets'],
        clientID: 'XXXXXXXXXXXXXXXXX.apps.googleusercontent.com',
    }).then(() => {
        GoogleSignIn.signInPromise().then((user) => {
            store.dispatch(auth(user.accessToken));
        }).catch((err) => {
            console.log(err);
        });
    }).catch((err) => {
        console.log(err);
    });
};

So then I have the user.accessToken in my redux store and can use it elsewhere to create REST requests to the google sheets API.

Here is a simple example of a component that handles the auth and then retrieves some data from the sheet:

// <project_folder>/src/main.js
import React, { Component } from 'react';
import {
    ActivityIndicator,
    Text,
    View,
    StyleSheet,
} from 'react-native';
import { connect } from 'react-redux';
import auth from './services/auth';
import Sheet from './services/sheet';

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});

const sheetId = 'XX-XXXXXX_XXX_XXXXXXXXXXXXXXXXXXXXXX';


class Main extends Component {
    constructor(props) {
        super(props);
        this.state = {
            animating: true,
        };
    }

    componentDidMount() {
        auth();
    }

    componentWillUpdate(nextProps) {
        if (this.props.token !== nextProps.token) this.setState({ animating: false });
    }

    componentDidUpdate(nextProps) {
        this.sheet = new Sheet(id, this.props.token);
        this.sheet.getDoc();
    }

    render() {
        return (
            <View style={styles.container}>
                <ActivityIndicator
                    animating={this.state.animating}
                    style={{ height: 80 }}
                    size="large"
                />
                {!this.state.animating &&
                    <Text>
                        {this.props.name}
                    </Text>
                }
            </View>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        token: state.auth.get('token'),
        name: state.sheet.get('name'),
    };
};

export default connect(mapStateToProps)(Main);

And here is a basic service to read/write a sheet:

// <project_folder>/services/sheet.js
import { store } from '../store';
import {
    nameGet,
    valueGet,
    valuePost,
}
from '../actions/sheet';

class Sheet {
    constructor(id, token) {
        this.id = id;
        this.token = token;
        this.endPoint = `https://sheets.googleapis.com/v4/spreadsheets/${this.id}`;
    }

    getDoc() {
        fetch(`${this.endPoint}?access_token=${this.token}`).then((response) => {
            response.json().then((data) => {
                console.log(data);
                store.dispatch(nameGet(data.properties.title));
            });
        });
    }

    getCell(sheet, cell) {
        const range = `${sheet}!${cell}`;
        fetch(`${this.endPoint}/values/${range}?access_token=${this.token}`)
        .then((response) => {
            response.json()
            .then((data) => {
                console.log(data);
                store.dispatch(valueGet(data.values[0][0]));
            })
            .catch((err) => {
                console.log(err);
            });
        })
        .catch((err) => {
            console.log(err);
        });
    }


    writeCell(sheet, cell, value) {
        const range = `${sheet}!${cell}`;
        const body = JSON.stringify({ values: [[value]] });
        fetch(
            `${this.endPoint}/values/${range}?valueInputOption=USER_ENTERED&access_token=${this.token}`,
            {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body,
            }
        )
        .then((response) => {
            response.json()
            .then((data) => {
                console.log(data);
                store.dispatch(valuePost('OK'));
            })
            .catch((err) => {
                console.log(err);
            });
        })
        .catch((err) => {
            console.log(err);
        });
    }
}

export default Sheet;

If there is a better way to do, please let me know.

like image 136
xavier.seignard Avatar answered Oct 21 '22 17:10

xavier.seignard


I have no fully idea about this but yes if you can set the sheet permission as public and access from your application it will not ask you authentication.

also you may use below link may help more technical Google Sheets API v4

like image 43
Arpan24x7 Avatar answered Oct 21 '22 15:10

Arpan24x7