Skip to content

Commit

Permalink
feat: add support for logins via GitHub and Microsoft (#53)
Browse files Browse the repository at this point in the history
* feat: refactor Plugins.js to handle multiple social logins

Added support of GitHub and Microsoft

* docs($readme): added info about GitHub and Microsoft support

Updated README.md  with info about GitHub and Microsoft support

no breaking changes
  • Loading branch information
vikmaksymenko committed Sep 4, 2020
1 parent 9b755da commit d3ea3e5
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 86 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,6 @@ typings/

# DynamoDB Local files
.dynamodb/

# IntelliJ IDEA files
.idea
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ It does so by delegating the login process to a `puppeteer` flow that performs t

Supported identity providers:

| Provider | Plugin name |
| -------- | ----------------- |
| Google | GoogleSocialLogin |
| GitHub | TBD |
| Facebook | TBD |
| Twitter | TBD |
| LinkedIn | TBD |
| Provider | Plugin name |
| --------- | --------------------- |
| Google | GoogleSocialLogin |
| GitHub | GitHubSocialLogin |
| Microsoft | MicrosoftSocialLogin |
| Facebook | TBD |
| Twitter | TBD |
| LinkedIn | TBD |

# Usage

Expand Down
210 changes: 131 additions & 79 deletions src/Plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,69 +21,6 @@ const puppeteer = require('puppeteer')
* @param {options.cookieDelay} number delay a specific milliseconds before get a cookies. Pass a falsy (false, 0, null, undefined, '') to avoid completely.
*
*/
module.exports.GoogleSocialLogin = async function GoogleSocialLogin(options = {}) {
validateOptions(options)

const launchOptions = {headless: !!options.headless}

if (options.args && options.args.length) {
launchOptions.args = options.args
}

const browser = await puppeteer.launch(launchOptions)
let page = await browser.newPage()
let originalPageIndex = 1
await page.setViewport({width: 1280, height: 800})
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-US;q=0.9,en;q=0.8'
})
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
)

await page.goto(options.loginUrl)
await login({page, options})

// Switch to Popup Window
if (options.isPopup) {
if (options.popupDelay) {
await delay(options.popupDelay)
}
const pages = await browser.pages()
// remember original window index
originalPageIndex = pages.indexOf(
pages.find(p => page._target._targetId === p._target._targetId)
)
page = pages[pages.length - 1]
}

await typeUsername({page, options})
await typePassword({page, options})

// Switch back to Original Window
if (options.isPopup) {
if (options.popupDelay) {
await delay(options.popupDelay)
}
const pages = await browser.pages()
page = pages[originalPageIndex]
}

if (options.cookieDelay) {
await delay(options.cookieDelay)
}

const cookies = await getCookies({page, options})
const lsd = await getLocalStorageData({page, options})
const ssd = await getSessionStorageData({page, options})
await finalizeSession({page, browser, options})

return {
cookies,
lsd,
ssd
}
}

function delay(time) {
return new Promise(function(resolve) {
Expand Down Expand Up @@ -112,22 +49,6 @@ async function login({page, options} = {}) {
await page.click(options.loginSelector)
}

async function typeUsername({page, options} = {}) {
await page.waitForSelector('input[type="email"]')
await page.type('input[type="email"]', options.username)
await page.click('#identifierNext')
}

async function typePassword({page, options} = {}) {
let buttonSelectors = ['#signIn', '#passwordNext', '#submit']

await page.waitForSelector('input[type="password"]', {visible: true})
await page.type('input[type="password"]', options.password)

const buttonSelector = await waitForMultipleSelectors(buttonSelectors, {visible: true}, page)
await page.click(buttonSelector)
}

async function getCookies({page, options} = {}) {
await page.waitForSelector(options.postLoginSelector)

Expand Down Expand Up @@ -218,3 +139,134 @@ async function racePromises(promises) {
return index
})
}

async function baseLoginConnect(typeUsername, typePassword, authorizeApp, options) {
validateOptions(options)

const launchOptions = {headless: !!options.headless}

if (options.args && options.args.length) {
launchOptions.args = options.args
}

const browser = await puppeteer.launch(launchOptions)
let page = await browser.newPage()
let originalPageIndex = 1
await page.setViewport({width: 1280, height: 800})
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-USq=0.9,enq=0.8'
})
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0 Win64 x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
)

await page.goto(options.loginUrl)
await login({page, options})

// Switch to Popup Window
if (options.isPopup) {
if (options.popupDelay) {
await delay(options.popupDelay)
}
const pages = await browser.pages()
// remember original window index
originalPageIndex = pages.indexOf(
pages.find(p => page._target._targetId === p._target._targetId)
)
page = pages[pages.length - 1]
}

await typeUsername({page, options})
await typePassword({page, options})

if (options.authorize) {
authorizeApp({page, options})
}

// Switch back to Original Window
if (options.isPopup) {
if (options.popupDelay) {
await delay(options.popupDelay)
}
const pages = await browser.pages()
page = pages[originalPageIndex]
}

if (options.cookieDelay) {
await delay(options.cookieDelay)
}

const cookies = await getCookies({page, options})
const lsd = await getLocalStorageData({page, options})
const ssd = await getSessionStorageData({page, options})
await finalizeSession({page, browser, options})

return {
cookies,
lsd,
ssd
}
}

module.exports.GoogleSocialLogin = async function GoogleSocialLogin(options = {}) {
const typeUsername = async function({page, options} = {}) {
await page.waitForSelector('input[type="email"]')
await page.type('input[type="email"]', options.username)
await page.click('#identifierNext')
}

const typePassword = async function({page, options} = {}) {
let buttonSelectors = ['#signIn', '#passwordNext', '#submit']

await page.waitForSelector('input[type="password"]', {visible: true})
await page.type('input[type="password"]', options.password)

const buttonSelector = await waitForMultipleSelectors(buttonSelectors, {visible: true}, page)
await page.click(buttonSelector)
}

return baseLoginConnect(typeUsername, typePassword, null, options)
}

module.exports.GitHubSocialLogin = async function GitHubSocialLogin(options = {}) {
const typeUsername = async function({page, options} = {}) {
await page.waitForSelector('input#login_field')
await page.type('input#login_field', options.username)
}

const typePassword = async function({page, options} = {}) {
await page.waitForSelector('input#password', {visible: true})
await page.type('input#password', options.password)
await page.click('input[type="submit"]')
}

const authorizeApp = async function({page, options} = {}) {
await page.waitForSelector('button#js-oauth-authorize-btn', {visible: true})
await page.click('button#js-oauth-authorize-btn', options.password)
}

return baseLoginConnect(typeUsername, typePassword, authorizeApp, options)
}

module.exports.MicrosoftSocialLogin = async function MicrosoftSocialLogin(options = {}) {
const typeUsername = async function({page, options} = {}) {
await page.waitForSelector('input[type="email"]')
await page.type('input[type="email"]', options.username)
await page.click('input[type="submit"]')
}

const typePassword = async function({page, options} = {}) {
await delay(5000)

await page.waitForSelector('input[type="password"]', {visible: true})
await page.type('input[type="password"]', options.password)
await page.click('input[type="submit"]')
}

const authorizeApp = async function({page, options} = {}) {
await page.waitForSelector('button#js-oauth-authorize-btn', {visible: true})
await page.click('button#js-oauth-authorize-btn', options.password)
}

return baseLoginConnect(typeUsername, typePassword, authorizeApp, options)
}

0 comments on commit d3ea3e5

Please sign in to comment.