Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

login using google account #18580

Closed
kedevked opened this issue Nov 4, 2022 · 6 comments
Closed

login using google account #18580

kedevked opened this issue Nov 4, 2022 · 6 comments

Comments

@kedevked
Copy link

kedevked commented Nov 4, 2022

Discussed in https://github.com/microsoft/playwright/discussions/18402

Originally posted by kedevked October 28, 2022
I am trying to login on a website that requires the user to login on google first. Using cypress, I am able to write the test as follows

it('should login', () => {
cy.request({
    method: 'POST',
    url: 'https://www.googleapis.com/oauth2/v4/token',
    body: {
      grant_type: 'refresh_token',
      client_id: 'client_id',
      client_secret: 'client_secret',
      refresh_token: 'refresh_token',
    },
  }).then(({ body }) => {
    const { access_token, id_token } = body;

    cy.request({
      method: 'GET',
      url: 'https://www.googleapis.com/oauth2/v3/userinfo',
      headers: { Authorization: `Bearer ${access_token}` },
    }).then(({ body }) => {
      cy.log(body);
      const userItem = {
        token: id_token,
        user: {
          googleId: body.sub,
          email: body.email,
          givenName: body.given_name,
          familyName: body.family_name,
          imageUrl: body.picture,
        },
      };

      cy.visit(settings.website);
      cy.contains('is logged in')
})

With playwrite, I wrote the following spec:

test("should log user in", async ({ page }) => {

  // const requestContext = await page.request.newContext();
  const response = await page.request.post(
    "https://www.googleapis.com/oauth2/v4/token",
    {
      data: {
        grant_type: "grant_type",
        client_id:
          "client_id",
        client_secret: "client_secret",
        refresh_token:
          "refresh_token",
      },
    }
  );
  const content = await response.json();
  const { access_token, id_token } = content;

  const rawResponse2 = await page.request.post(
    "https://www.googleapis.com/oauth2/v3/userinfo",
    {
      headers: { Authorization: `Bearer ${access_token}` },
    }
  );

  const data = (await rawResponse2.json()) as any;

  const userItem = {
    token: id_token,
    user: {
      googleId: data.sub,
      email: data.email,
      givenName: data.given_name,
      familyName: data.family_name,
      imageUrl: data.picture,
    },
  };
  console.log("userItem", userItem); // userItem is well displayed in the console

  await page.goto(settings.website);

  await expect(page).toContain("is logged in"); // test fails, the user is still not logged in
});

I wonder what I am doing wrong

@yury-s
Copy link
Member

yury-s commented Nov 8, 2022

I wonder what I am doing wrong

Looks like you are doing post request second time instead of get, try const rawResponse2 = await page.request.get(.

If that doesn't help, we need more details about how userItem is used in the code (except for printing it to console).

@yury-s yury-s added the triaging label Nov 8, 2022
@kedevked
Copy link
Author

kedevked commented Nov 9, 2022

That's a typo. I changed get by post but the issue is still the same.
Let's me now explain further my webapp. The user navigates to the website, then a request is made to firebase. If that request succeeds, then the user is directly logged in; if not, a signing button is displayed to the user. Clicking on that button prompts the google login popup.
Here is my complete setup in playwright

async function globalSetup(config: FullConfig) {
    const browser = await chromium.launch();
    const requestContext = await browser.newContext();
    const page = await browser.newPage();
  
    const response =await requestContext.request.post("https://www.googleapis.com/oauth2/v4/token",
    {
      data: {
        grant_type: " grant_type",
        client_id:
          "client_id",
        client_secret: "client_secret",
        refresh_token:
          "refresh_token",
      },
    })
  
    const content = await response.json();
    const { access_token, id_token } = content;
  
    const rawResponse2 = await requestContext.request.get(
      "https://www.googleapis.com/oauth2/v3/userinfo",
      {
        headers: { Authorization: `Bearer ${access_token}` },
      }
    );
  
    const data = (await rawResponse2.json()) as any;
  
    const userItem = {
      token: id_token,
      user: {
        googleId: data.sub,
        email: data.email,
        givenName: data.given_name,
        familyName: data.family_name,
        imageUrl: data.picture,
      },
    };
    
    const credential = GoogleAuthProvider.credential(id_token);
  
    // Sign in with credential from the Google user.
    const auth = getAuth();
    await signInWithCredential(auth, credential);
    const cookies = await page.request.storageState({ path: "storageState.json" });
    // nothing is stored in the storageState.json; I don't know if it is relevant though
    
    await page.goto("http://localhost:4200");
    // I expect the user to be logged in here because he has already connected to the google account
    // but it is failing
    
    await browser.close();
  }
// spec file
test.describe("login", () => {
  test("should log user in", async ({ page }) => {
    await expect(page).toContain("title");
  });
});

  

In comparison with cypress, my whole code is as follows:

Cypress.Commands.add("loginByGoogleApi", () => {
  cy.log("Logging in to Google");
  cy.request({
    method: "POST",
    url: "https://www.googleapis.com/oauth2/v4/token",
    body: {
      grant_type: "grant_type",
      client_id: "client_id",
      client_secret: "client_secret",
      refresh_token: "refresh_token",
    },
  }).then(({ body }) => {
    const { access_token, id_token } = body;

    cy.request({
      method: "GET",
      url: "https://www.googleapis.com/oauth2/v3/userinfo",
      headers: { Authorization: `Bearer ${access_token}` },
    }).then(({ body }) => {
      cy.log(body);
      const userItem = {
        token: id_token,
        user: {
          googleId: body.sub,
          email: body.email,
          givenName: body.given_name,
          familyName: body.family_name,
          imageUrl: body.picture,
        },
      };

      // Build Firebase credential with the Google ID token.
      const credential = GoogleAuthProvider.credential(id_token);

      // Sign in with credential from the Google user.
      const auth = getAuth();
      await signInWithCredential(auth, credential)

      cy.visit(settings.website);
    });
  });
});

// spec file
it('login should passed', () => {
    cy.contains('title').should('be.visible');
  });

@yury-s
Copy link
Member

yury-s commented Nov 9, 2022

const cookies = await page.request.storageState({ path: "storageState.json" });

// nothing is stored in the storageState.json; I don't know if it is relevant though

This is the root cause. And the reason it happens is that you use page that is created in via const page = await browser.newPage(); rather than const page = await requestContext.newPage();. The former will create a new context underneath and create a page in it. Replacing that line with const page = await requestContext.newPage(); should fix the issue. You can also do requestContext.request.storageState(... to overcome that.

I also seems that you only do API calls and don't actually need to start browser for that, you can try using global request context for that:

import { request } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const requestContext = await request.newContext();

  const response =await requestContext.post("https://www.googleapis.com/oauth2/v4/token",
  {
    data: {
      grant_type: " grant_type",
      client_id:
        "client_id",
      client_secret: "client_secret",
      refresh_token:
        "refresh_token",
    },
  })

  const content = await response.json();
  const { access_token, id_token } = content;

  const rawResponse2 = await requestContext.get(
    "https://www.googleapis.com/oauth2/v3/userinfo",
    {
      headers: { Authorization: `Bearer ${access_token}` },
    }
  );

  const data = (await rawResponse2.json()) as any;

  const userItem = {
    token: id_token,
    user: {
      googleId: data.sub,
      email: data.email,
      givenName: data.given_name,
      familyName: data.family_name,
      imageUrl: data.picture,
    },
  };
  
  const credential = GoogleAuthProvider.credential(id_token);

  // Sign in with credential from the Google user.
  const auth = getAuth();
  await signInWithCredential(auth, credential);
  const cookies = await requestContext.storageState({ path: "storageState.json" });
  // ...
}

Let us know if this works.

@kedevked
Copy link
Author

kedevked commented Nov 10, 2022

const requestContext = await request.newContext();
const page = await requestContext.newPage();

I have the error:

Property 'newPage' does not exist on type 'APIRequestContext'.

@yury-s
Copy link
Member

yury-s commented Nov 11, 2022

Well, the last example I provided does not create Browser and BrowserContext, requestContext is an instance of APIRequestContext. The comment was for your original snippet where you should change beginning of the method like this:

async function globalSetup(config: FullConfig) {
    const browser = await chromium.launch();
    const requestContext = await browser.newContext();
    const page = await requestContext.newPage();
...

@yury-s yury-s closed this as completed Nov 11, 2022
@kedevked
Copy link
Author

kedevked commented Nov 11, 2022

I tried to use both approaches, the global request context and the browser.NewContext. But none of the two approaches works.
Nothing is saved to the storageState.json file.
@yury-s do I need to provide a real testing website to demonstrate it for the issue to be reopened ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants