MSAL and OAuth 2.0 - Request an authorization code programmatically - cypress

Goal is to get access token from MSAL programmatically for Cypress e2e tests.
We use V2.0 API.
According to this I first need to get the authorization code: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
to get the access token https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-access-token
So in order to get authorization code I would need to do this request
// GET
// Line breaks for legibility only
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&state=12345
&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
&code_challenge_method=S256
But this returns text/html so I would need to manually login to get the code.
Is there any way to progammatically to get the authorization code?

This is how I got it solved by creating a login command. The command fetches the token programatically and stores it into localStorage.
import 'cypress-localstorage-commands';
Cypress.Commands.add('login', () => {
const request = {
method: 'POST',
form: true,
url: `https://login.microsoftonline.com/${Cypress.config('tenantId')}/oauth2/v2.0/token`,
body: {
grant_type: 'client_credentials',
client_id: Cypress.config('clientId'),
client_secret: Cypress.config('clientSecret'),
scope: `${Cypress.config('clientId')}/.default`,
},
};
cy.request(request).then(response => cy.setLocalStorage('msal.idtoken', response.body.access_token));
});

Related

javascript client for oauth2 and google-oauth library's "redirect-uri" missing case

I am working on porting oauth2client library to google-auth and oauthlib library
Right now for oauth2 authentication, I am using javascript client and using grantofflineaccess method in it.
The flow for oauth2client library was:
using the js client, we get the authorization code and pass it in oauth2client.client.credentials_from_code to get the credentials object. In the paramaeters, we pass only clientid,secret,scopes and code. There was no mention of redirect_uri.
Right now I am replacing it with flow
https://google-auth-oauthlib.readthedocs.io/en/latest/reference/google_auth_oauthlib.flow.html
This means that I HAVE TO pass redirect_uri to create the flow object, following which I can use flow.fetch_token(code) to get flow.credentials.
Here is my javascript side code (angularJS) (Reference: https://developers.google.com/identity/sign-in/web/reference)
// init
gapi.load('auth2', () => {
const authScopes = {
client_id: $window.GOOGLE_API_CLIENT_ID,
scope: scopes.join(' '),
};
auth2 = gapi.auth2.init(authScopes);
// When signin button clicked
auth2.grantOfflineAccess({ redirect_uri: 'postmessage', prompt: 'consent' })
.then((response) => {
// We enter this when user signins into google account and 'ALLOWS' the scopes. We receive the code in response using which we get the access and refresh token. The below calls are being made to backend
Something.authenticateUser(response.code).then(() => {
$scope.sheetsWizard.onLoginSuccess();
The backend code: (django)
# Upon receiving the 'code', it calls oauth2client function to get credentials object
credentials = client.credentials_from_code(
GOOGLE_API_CLIENT_ID,
GOOGLE_API_CLIENT_SECRET,
scopes,
data['code']
The above was older code, How the new code looks in the backend (The frontend or angularJS remains the same)
flow = Flow.from_client_config(client_config=dictionary_with_client_credentials, scopes=scopes, redirect_uri='some_website.com')
flow.fetch_token(code = data['code'])
credentials = flow.credentials
My questions:
Is there a method in google-auth that I am missing, so I don't have to pass redirect_uri, and still get at least access_token and refresh_token, after receiving code?
in js gapi client, there is a mention of redirect_uri='postmessage', but could not find documentation related to it. What does it mean?

How to integrate AWS API Gateway API Key with Axios | CORS Error

I have built an API in AWS API Gateway. I have written the endpoints to perform basic CRUD operations as well. I am making a call to those endpoints using axios from my React frontend. The APIs in turn call AWS Lambda functions to interact with DynamoDB.
Since DynamoDB contains sensitive user data, I wish to secure it with an API key.
As per the steps mentioned here and here.
Now in order to make an API call I had the following code. Please note that I have swapped in the real values with dummy values for explanation purposes.
src/config/api.js
const customHeaders = {
"X-Api-Key": "thisIsADummyStringForExplanation",
"Content-Type": "application/json",
};
const axiosInstance = axios.create({
baseURL: "https://this.is.a.dummy.base.url/v0",
headers: customHeaders,
});
const Aws_Api_Gateway_GET = (uri) => {
return axiosInstance({
method: "get",
url: `${uri}`,
timeout: 2000,
});
};
export { Aws_Api_Gateway_GET };
Following is the Code that I wrote in order to make a GET request at the API endpoint
Aws_Api_Gateway_GET("/my-resource")
.then((res) => {
console.log(res);
})
.catch((err) => {
console.error(err);
});
THE ISSUE
This code throws CORS Error. I can assure that I have enabled CORS on the API Gateway by selecting the Enable CORS option for each and every resource.
Following is the error
Access to XMLHttpRequest at 'https://this.is.a.dummy.base.url/v0/my-resource' from origin 'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
But when I try the same using Postman, it works.
Can someone please help me get rid of the CORS Error ?
Thanks in advance.

Apollo Client: Send different headers relative to authentication/authorization

I will often have an expired authorization token in my app.
I do not, however, want this error to block requests that do not require authorization. What is the work around?
I'd like to customize my headers for requests to simply view a page (which doesn't require token, so send with an empty header) and for requests to edit data (add token and allow error to block request).
An invalid token, with headers set like below for every request, is now blocking the simple fetching of open data:
const client = new ApolloClient({
uri: "http://localhost:8000/graphql",
request: operation => {
const token = sessionStorage.getItem('jwtToken');
operation.setContext({
headers: {
'x-token': token || '',
},
});
},
});

Okta Authentication works but Get User by Id gives Invalid Token Provided

I have a Django app that authenticates using Okta:
headers = {
'Authorization': 'SSWS {}'.format(<okta api token>),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
authentication_payload = {
'username': <username>,
'password': <password>
}
response = requests.post(
<okta auth endpoint>,
headers=headers,
data=json.dumps(authentication_payload)
)
This works successfully. From the response content I am able to get the User Id:
content = json.loads(r.content.decode('utf-8'))
okta_user_id = content['_embedded']['user']['id']
I then use the okta_user_id to create the endpoint to get the okta user by id:
okta_user_endpoint = https://<org>.okta.com/api/v1/users/<okta_user_id>
I then use the same headers from the authentication call, with the same api token, and try to get the user by id:
user_response = requests.get(
okta_user_endpoint,
headers=headers
)
But this is unsuccessful. I get a 401 error with the following content:
{
"errorCode":"E0000011",
"errorSummary":"Invalid token provided",
"errorLink":"E0000011",
"errorCauses":[]
}
Seems straight forward with an invalid token, but if the token is invalid how am I able to successfully make the authentication call? And if the token if valid for the authentication call why is it not working to get the user by id?
Okta recently changed the way that the /authn endpoint works. The /authn endpoint no longer requires an authentication token. This was done in order to support single-page applications.
It looks like your application will need to be able to fetch user information on an arbitrary user. In that case, using an Okta API token makes sense.
However, if you were making that call from a single-page application, you would want to make a request to the /users/me API endpoint.

Cookie is "undefined" in Koa/Express request

I'm using Koa JS framework for jwt authentication.
So basically when the user signs in, I set a jwt token (signed) to user's browser cookie, which seems to work fine as shown below (Chrome cookie settings):
(www.localhost.com instead of localhost is because I edited my hostfile, but this should have no effect in setting/getting cookies)
The problem, however, is when I send a POST request to my local Koa server, the jwt cookie is undefined. All I'm doing to verify the token is this:
routes.js
const Router = require("koa-router");
const router = new Router();
router.post(`api/authenticate`, function* () {
const jwt = this.cookies.get("jwt", { signed: true }); //jwt is undefined!!
if (!jwt)
this.throw("Invalid or expired token!");
this.status = 200;
});
//...
app.use(router.routes());
app.use(router.allowedMethods());
Here, this.cookies.get("jwt") returns undefined. The POST request is sent using AXIOS library with "withCredentials: true" header and a valid CSRF token:
authenticate.js
axios.post("api/authenticate", {}, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": "A VALID CSRF TOKEN GENERATED BY SERVER",
"Content-Type": "application/json",
},
withCredentials: true,
});
Can anyone help me find out why this.cookies.get fails to fetch cookie from a simple POST request? I'm simply posting to my localhost, so I believe this is not a CORS problem.
What is more strange is that when I check my chrome developer tool, the "jwt" and "jwt.sig" tokens are successfully included in the request header..
Any help would be appreciated.
Update: Setting the cookie
//...
this.cookies.set("jwt", "SOME JWT GENERATED BY SERVER", {
httpOnly: true,
signed: true,
});
//...

Resources