Skip to content

feat: oauth test cases #297

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

Merged
merged 7 commits into from
Mar 17, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/core/contentstackHTTPClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ export default function contentstackHttpClient (options) {
let uiHostName = hostname
let developerHubBaseUrl = hostname

if (hostname.endsWith('io')) {
uiHostName = hostname.replace('io', 'com')
if (uiHostName?.endsWith('io')) {
uiHostName = uiHostName.replace('io', 'com')
}

if (hostname.startsWith('api')) {
if (uiHostName?.startsWith('api')) {
uiHostName = uiHostName.replace('api', 'app')
}
const uiBaseUrl = config.endpoint || `${protocol}://${uiHostName}`
Expand Down
144 changes: 144 additions & 0 deletions test/sanity-check/api/oauth-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { expect } from 'chai'
import { describe, it } from 'mocha'
import { contentstackClient } from '../../sanity-check/utility/ContentstackClient'
import axios from 'axios'
import dotenv from 'dotenv'

dotenv.config()
let accessToken = ''
let loggedinUserID = ''
let authUrl = ''
let codeChallenge = ''
let codeChallengeMethod = ''
let authCode
let authtoken = ''
let redirectUrl = ''
let refreshToken = ''
const client = contentstackClient()
const oauthClient = client.oauth({
clientId: process.env.CLIENT_ID,
appId: process.env.APP_ID,
redirectUri: process.env.REDIRECT_URI
})

describe('OAuth Authentication API Test', () => {
it('should login with credentials', done => {
client.login({ email: process.env.EMAIL, password: process.env.PASSWORD }, { include_orgs: true, include_orgs_roles: true, include_stack_roles: true, include_user_settings: true }).then((response) => {
expect(response.notice).to.be.equal('Login Successful.', 'Login success messsage does not match.')
done()
})
.catch(done)
})

it('should get Current user info test', done => {
client.getUser().then((user) => {
authtoken = user.authtoken
done()
})
.catch(done)
})

it('should fail when trying to login with invalid app credentials', () => {
try {
client.oauth({
clientId: 'clientId',
appId: 'appId',
redirectUri: 'redirectUri'
})
} catch (error) {
const jsonMessage = JSON.parse(error.message)
expect(jsonMessage.status).to.be.equal(401, 'Status code does not match for invalid credentials')
expect(jsonMessage.errorMessage).to.not.equal(null, 'Error message not proper')
expect(jsonMessage.errorCode).to.be.equal(104, 'Error code does not match')
}
})

it('should generate OAuth authorization URL', async () => {
authUrl = await oauthClient.authorize()
const url = new URL(authUrl)

codeChallenge = url.searchParams.get('code_challenge')
codeChallengeMethod = url.searchParams.get('code_challenge_method')

// Ensure they are not empty strings
expect(codeChallenge).to.not.equal('')
expect(codeChallengeMethod).to.not.equal('')
expect(authUrl).to.include(process.env.CLIENT_ID, 'Client ID mismatch')
})

it('should simulate calling the authorization URL and receive authorization code', async () => {
try {
const authorizationEndpoint = oauthClient.axiosInstance.defaults.developerHubBaseUrl
axios.defaults.headers.common.authtoken = authtoken
axios.defaults.headers.common.organization_uid = process.env.ORGANIZATION
const response = await axios
.post(`${authorizationEndpoint}/manifests/${process.env.APP_ID}/authorize`, {
client_id: process.env.CLIENT_ID,
redirect_uri: process.env.REDIRECT_URI,
code_challenge: codeChallenge,
code_challenge_method: codeChallengeMethod,
response_type: 'code'
})
const data = response.data
redirectUrl = data.data.redirect_url
const url = new URL(redirectUrl)
authCode = url.searchParams.get('code')
oauthClient.axiosInstance.oauth.appId = process.env.APP_ID
oauthClient.axiosInstance.oauth.clientId = process.env.CLIENT_ID
oauthClient.axiosInstance.oauth.redirectUri = process.env.REDIRECT_URI
// Ensure they are not empty strings
expect(redirectUrl).to.not.equal('')
expect(url).to.not.equal('')
} catch (error) {
console.log(error)
}
})

it('should exchange authorization code for access token', async () => {
const response = await oauthClient.exchangeCodeForToken(authCode)
accessToken = response.access_token
loggedinUserID = response.user_uid
refreshToken = response.refresh_token

expect(response.organization_uid).to.be.equal(process.env.ORGANIZATION, 'Organization mismatch')
// eslint-disable-next-line no-unused-expressions
expect(response.access_token).to.not.be.null
// eslint-disable-next-line no-unused-expressions
expect(response.refresh_token).to.not.be.null
})

it('should get the logged-in user info using the access token', async () => {
const user = await client.getUser({
authorization: `Bearer ${accessToken}`
})
expect(user.uid).to.be.equal(loggedinUserID)
expect(user.email).to.be.equal(process.env.EMAIL, 'Email mismatch')
})

it('should refresh the access token using refresh token', async () => {
const response = await oauthClient.refreshAccessToken(refreshToken)

accessToken = response.access_token
refreshToken = response.refresh_token
// eslint-disable-next-line no-unused-expressions
expect(response.access_token).to.not.be.null
// eslint-disable-next-line no-unused-expressions
expect(response.refresh_token).to.not.be.null
})

it('should logout successfully after OAuth authentication', async () => {
const response = await oauthClient.logout()
expect(response).to.be.equal('Logged out successfully')
})

it('should fail to make an API request with an expired token', async () => {
try {
await client.getUser({
authorization: `Bearer ${accessToken}`
})
} catch (error) {
expect(error.status).to.be.equal(401, 'API request should fail with status 401')
expect(error.errorMessage).to.be.equal('The provided access token is invalid or expired or revoked', 'Error message mismatch')
}
})
})
1 change: 1 addition & 0 deletions test/sanity-check/sanity.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ require('./api/contentType-delete-test')
require('./api/delete-test')
require('./api/team-test')
require('./api/auditlog-test')
require('./api/oauth-test')
1 change: 1 addition & 0 deletions test/unit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ require('./variantGroup-test')
require('./ungroupedVariants-test')
require('./variantsWithVariantsGroup-test')
require('./variants-entry-test')
require('./oauthHandler-test')
Loading