Skip to content

Commit

Permalink
Change login flow (experimental) (#223)
Browse files Browse the repository at this point in the history
This is the flow (more or less):

`clasp login`: logs you in using the default `clasp` credentials. Saves `.clasprc.json` to your `~` directory.
`clasp login --creds`: logs you in using the file `credentials.json` in the directory that you are running the command in. Saves `.clasprc.json` to your current directory.
`clasp login --creds other_creds.json`: logs you in using the credentials in `other_creds.json`. Saves `.clasprc.json` to your current directory.

Your credentials file should look like this:

```
{"installed":{"client_id":"239267426989-275aglft6htcfsdj7t5d3csdlogchamh.apps.googleusercontent.com","project_id":"project-id-xxxxxxxxx","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"xxxxxxxx","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}
```

Signed-off-by: campionfellin <[email protected]>

Related to #204 

- [x] `npm run test` succeeds.
- [x] `npm run lint` succeeds.
- [ ] Appropriate changes to README are included in PR.
  • Loading branch information
campionfellin authored and grant committed Jun 25, 2018
1 parent 304978f commit 0ce9d5d
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
6 changes: 4 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,16 @@ commander
* Logs the user in. Saves the client credentials to an rc file.
* @name login
* @param {string?} [--no-localhost] Do not run a local server, manually enter code instead.
* @param {string?} [--ownkey] Save .clasprc.json file to current working directory.
* @param {string?} [--creds] Relative path to credentials (from GCP).
* @example login (uses default clasp credentials)
* @example login --creds credentials.json (uses your credentials file).
* @see test
*/
commander
.command('login')
.description('Log in to script.google.com')
.option('--no-localhost', 'Do not run a local server, manually enter code instead')
.option('--ownkey', 'Save .clasprc.json file to current working directory')
.option('--creds <file>', 'Relative path to credentials (from GCP).')
.action(login);

/**
Expand Down
34 changes: 28 additions & 6 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Script } from 'googleapis/build/src/apis/script/v1';
import { ClaspSettings, DOTFILE, ERROR, LOG, checkIfOnline, logError } from './utils';
import open = require('open');
import readline = require('readline');
import * as fs from 'fs';

// API settings
// @see https://developers.google.com/oauthplayground/
Expand All @@ -32,7 +33,7 @@ const oauth2ClientSettings = {
clientSecret: 'v6V3fKV_zWU7iw1DrpO1rknX',
redirectUri: 'http://localhost',
};
export const oauth2Client = new OAuth2Client(oauth2ClientSettings);
const oauth2Client = new OAuth2Client(oauth2ClientSettings);

// Google API clients
export const script = google.script({
Expand All @@ -52,11 +53,30 @@ export const drive = google.drive({
* Requests authorization to manage Apps Script projects.
* @param {boolean} useLocalhost True if a local HTTP server should be run
* to handle the auth response. False if manual entry used.
* @param {string} creds location of credentials file.
*/
async function authorize(useLocalhost: boolean, writeToOwnKey: boolean) {
async function authorize(useLocalhost: boolean, creds: string) {
let ownCreds = false;
try {
const credentials = JSON.parse(fs.readFileSync(creds, 'utf8'));
if (credentials && credentials.installed && credentials.installed.client_id
&& credentials.installed.client_secret) {
oauth2ClientSettings.clientId = credentials.installed.client_id;
oauth2ClientSettings.clientSecret = credentials.installed.client_secret;
ownCreds = true;
console.log(LOG.CREDENTIALS_FOUND);
} else {
logError(null, ERROR.BAD_CREDENTIALS_FILE);
}
} catch(err) {
if (err.code === 'ENOENT') {
logError(null, ERROR.CREDENTIALS_DNE);
}
console.log(LOG.DEFAULT_CREDENTIALS);
}
try {
const token = await (useLocalhost ? authorizeWithLocalhost() : authorizeWithoutLocalhost());
await (writeToOwnKey ? DOTFILE.RC_LOCAL.write(token) : DOTFILE.RC.write(token));
await (ownCreds ? DOTFILE.RC_LOCAL.write(token) : DOTFILE.RC.write(token));
console.log(LOG.AUTH_SUCCESSFUL);
process.exit(0); // gracefully exit after successful login
} catch(err) {
Expand Down Expand Up @@ -141,17 +161,19 @@ async function authorizeWithoutLocalhost() {

/**
* Logs the user in. Saves the client credentials to an rc file.
* @param options the localhost and ownkey options from commander
* @param {object} options the localhost and creds options from commander.
* @param {boolean} options.localhost authorize without http server.
* @param {string} options.creds location of credentials file.
*/
export function login(options: { localhost: boolean, ownkey: boolean}) {
export function login(options: { localhost: boolean, creds: string}) {
DOTFILE.RC.read().then((rc: ClaspSettings) => {
console.warn(ERROR.LOGGED_IN);
}).catch(async (err: string) => {
DOTFILE.RC_LOCAL.read().then((rc: ClaspSettings) => {
console.warn(ERROR.LOGGED_IN);
}).catch(async (err: string) => {
await checkIfOnline();
authorize(options.localhost, options.ownkey);
authorize(options.localhost, options.creds);
});
});
}
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ export const DOTFILE = {
// Error messages (some errors take required params)
export const ERROR = {
ACCESS_TOKEN: `Error retrieving access token: `,
BAD_CREDENTIALS_FILE: 'Incorrect credentials file format.',
COMMAND_DNE: (command: string) => `🤔 Unknown command "${command}"\n
Forgot ${PROJECT_NAME} commands? Get help:\n ${PROJECT_NAME} --help`,
CONFLICTING_FILE_EXTENSION: (name: string) => `File names: ${name}.js/${name}.gs conflict. Only keep one.`,
CREATE: 'Error creating script.',
CREDENTIALS_DNE: 'Credentials file not found.',
DEPLOYMENT_COUNT: `Unable to deploy; Scripts may only have up to 20 versioned deployments at a time.`,
FOLDER_EXISTS: `Project file (${DOT.PROJECT.PATH}) already exists.`,
FS_DIR_WRITE: 'Could not create directory.',
Expand Down Expand Up @@ -120,6 +122,8 @@ export const LOG = {
CLONING: 'Cloning files...',
CREATE_PROJECT_FINISH: (scriptId: string) => `Created new script: ${getScriptURL(scriptId)}`,
CREATE_PROJECT_START: (title: string) => `Creating new script: ${title}...`,
CREDENTIALS_FOUND: 'Credentials found, using those to login...',
DEFAULT_CREDENTIALS: 'No credentials given, continuing with default...',
DEPLOYMENT_CREATE: 'Creating deployment...',
DEPLOYMENT_DNE: 'No deployed versions of script.',
DEPLOYMENT_LIST: (scriptId: string) => `Listing deployments...`,
Expand Down

0 comments on commit 0ce9d5d

Please sign in to comment.