Forgot password - Cognito Hosted UI and OIDC Endpoint Reference - amazon-web-services

I am trying to do my customised UI for Cognito Login and Forgot Password using this Stackoverflow Answer. I am able to do Login request and redirect. Also I am able to get the Reset Password code to the email. But Confirm password submit request is not working. Also I am not getting any proper error messages from the API End point. I will provide here Login API request and Confirm Password Request.
Login Submit Request:
exports.postLogin = async (req, res, next) => {
const CLIENT_ID = req.query.client_id;
const RESPONSE_TYPE = 'token';
const REDIRECT_URI = encodeURIComponent(req.query.redirect_uri);
const SCOPE = req.query.scope;
const AUTH_DOMAIN = "<domain>.<region>.amazoncognito.com";
const USERNAME = req.body.username;
const PASSWORD = req.body.password
const state = req.query.state;
const XSRFTOKEN = req.body._csrf
let form = {
'_csrf': XSRFTOKEN,
'username': `${USERNAME.toLowerCase()}`,
'password': `${PASSWORD}`,
}
const formData = querystring.stringify(form);
const contentLength = formData.length;
console.log(formData)
// Post CSRF Token and username/password to /login endpoint
const codeRequestUrl = `https://${AUTH_DOMAIN}/login?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${state}`;
console.log(codeRequestUrl)
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': `XSRF-TOKEN=${XSRFTOKEN}; Path=/; Secure; HttpOnly; SameSite=Lax`,
},
uri: codeRequestUrl,
body: formData,
method: 'POST'
}, (err, response, body) => {
console.log(body)
console.log(err);
console.log(response.headers.location);
console.log(response.statusCode)
res.redirect(response.headers.location);
});
};
The above API is working fine. But if the user credential is wrong, the API doesn't providing proper error message.
Confirm Password Submit Request:
exports.postConfirmForgotPassword = async (req, res, next) => {
const req_parsed = new Url(req.url);
const CLIENT_ID = req.query.client_id;
const RESPONSE_TYPE = 'token';
const REDIRECT_URI = encodeURIComponent(req.query.redirect_uri);
const AUTH_DOMAIN = "<domain>.<region>.amazoncognito.com";
const USERNAME = req.body.username;
const ConfirmationCode = req.body.code;
const Password = req.body.password;
const state = req.query.state;
const XSRFTOKEN = req.body._csrf
let form = {
'_csrf': XSRFTOKEN,
'confirmation-code': `${ConfirmationCode}`, // I tried it with sending as 'code', 'confirmationcode'. But nothing is working.
'password': `${Password}`,
'username': `${USERNAME}`
}
const formData = querystring.stringify(form);
const contentLength = formData.length;
console.log(formData)
// Post CSRF Token and username/password to /login endpoint
const codeRequestUrl = `https://${AUTH_DOMAIN}/confirmForgotPassword?response_type=${RESPONSE_TYPE}&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&state=${state}`;
console.log(codeRequestUrl)
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': `XSRF-TOKEN=${XSRFTOKEN}; Path=/; Secure; HttpOnly; SameSite=Lax`,
},
uri: codeRequestUrl,
body: formData,
method: 'POST'
}, (err, response, body) => {
console.log(err);
console.log(response.headers.location);
console.log(response.statusCode)
console.log(`/login${req_parsed.query}`)
res.redirect(`/login${req_parsed.query}`);
});
};
I tried it to send the request body as below too. But nothing is working. Also not seeing any documentation for this in Cognito Side.
let form = {
'_csrf': XSRFTOKEN,
'code': `${ConfirmationCode}`,
'password': `${Password}`,
'username': `${USERNAME}`
}
(or)
let form = {
'_csrf': XSRFTOKEN,
'confirmationcode': `${ConfirmationCode}`,
'password': `${Password}`,
'username': `${USERNAME}`
}
Also Is there any way to identify the proper error messages for the above APIs
Also I tried to call the API according to the AWS Cognito Documentation. But I am receiving bad request
const main = async () => {
try {
const AUTH_DOMAIN = "mydomain.regison.amazoncognito.com";
const request_url = `https://${AUTH_DOMAIN}/confirmforgotPassword`;
const payload = {
ClientId:"CLIENT_ID",
ConfirmationCode:"****",
Password:"***********",
Username:"user email id",
SecretHash:"XSRFTOKEN"
}
const response = await axios.post(request_url, payload);
console.log(response)
} catch (e) {
console.log(e)
}
}
main()

Related

AWS Lambda Rerouting after Twitter Authorizing

I implemented a twitter login authorizer and I put an API route as the callback.
The Lambda function evoked on that route is the following:
const loginTwitterCallback = async (e, context) => {
const fetch = (...args) =>
import("node-fetch").then(({ default: fetch }) => fetch(...args));
const state = e.queryStringParameters.state;
const code = e.queryStringParameters.code;
try {
await fetch(
"https://api.twitter.com/2/oauth2/token?code=" +
code +
"&grant_type=authorization_code&client_id=" +
process.env.TWITTER_CLIENT_ID +
"&code_verifier=jwqoijoiw&redirect_uri=" + MY REDIRECT URI,
{
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded",
},
}
)
.then((res) => {
return res.json();
})
.then(async (data) => {
const accessToken = data.access_token;
return {
headers: {
Location:
"http://127.0.0.1:3000/auth/social?type=twitter&access_token=" +
encodeURIComponent(accessToken),
},
body: null,
statusCode: 302,
};
});
} catch (err) {
console.log(err);
}
};
Basically the user should be re-routed to the front-end where another POST request will be made to the API which will make a request to the Twitter API with the Bearer token and update the database.
The point is, I'm not being redirected to the front-end in the first place and I don't understand how to fix it.
Thanks a lot in advance.

Postman. Getting "JSONError: No data" after sending Cognito token pre-request

let tokenUrl = 'https://my.url/oauth2/token';
let scope = 'pets/read pets/updage petId/read'
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic Base64Encode(client_id:client_secret)'}, // encoded manually beforehand
body: {
mode: 'formdata',
formdata: [
{ key: 'grant_type', value: 'client_credentials' },
{ key: 'scope', value: scope }
]
}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = response.json(),
newAccessToken = jsonResponse.access_token;
pm.environment.set('access_token', newAccessToken);
pm.variables.set('access_token', newAccessToken);
});
Geeks, help, please!
I have API with Cognito authorization (Client Credentials type). It work's fine in Postman with manually 'Request new Access token'. But I want to retrieve token with pre-request script. I relied on AWS documentation about token endpoint. I have
JSONError: No data, empty input at 1:1
in the console. Do you have any suggestions?
I've encountered the same problem and resolved it by setting grant_type and scope as query string in the url:
let tokenUrl = pm.variables.get("cognito-url")+ "/oauth2/token?grant_type=client_credentials&scope=' + pm.variables.get("cognito-scope");
let auth_code = btoa(pm.variables.get("cognito-client-id") + ":" + pm.variables.get("cognito-client-secret"))
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + auth_code}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = response.json(),
newAccessToken = jsonResponse.access_token;
pm.environment.set('access_token', newAccessToken);
pm.variables.set('access_token', newAccessToken);
});

How to mutate apollo context in next.js?

I've looked in to api-routes-apollo-server-and-client-auth example and not sure how can i mutate context both on ssr and client request.
I want for every graphql resolver (using api) have access to ctx.user object with preparsed JWT token. But where should i parse it?
If i parse it here:
const apolloServer = new ApolloServer({
schema,
context(ctx) {
ctx.user = '123'
return ctx
}
})
export default apolloServer.createHandler({ path: '/api/graphql' })
Then ctx.user will be undefined on SSR request, working only on client request.
You can have something like this:
// server
const apolloServer = new ApolloServer({
schema,
context: async () => {
const jwt = req.headers.authorization
const user = await getUserFromJwt(jwt)
return {
user,
}
}
})
// client
const link = createHttpLink({ uri: 'http://localhost...' })
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: jwt,
},
}
})
const apolloClient = new ApolloClient({
link: authLink.concat(link),
...
})
Check out these docs: client and server

503 error in authentication using Cloudfront and AWS Lambda for S3 hosted website

I associated the following AWS Lambda function with CloudFront to add authentication to a static website hosted in Amazon S3, the problem with the for loop, if I test against a single username and password it works fine, If I add multiple credentials I got 503 Error.
exports.handler = (event, context, callback) => {
// Get request and request headers
const request = event.Records[0].cf.request;
const headers = request.headers;
// Configure authentication
const credentials = {
'admin1': 'passadmin2',
'user1': 'passuser1',
'admin2': 'passadmin2',
'user2': 'passuser2'
};
let authenticated = true;
//verify the Basic Auth string
for (let username in credentials) {
// Build a Basic Authentication string
let authString = 'Basic ' + Buffer.from(username + ':' + credentials[username]).toString('base64');
if (headers.authorization[0].value == authString) {
// User has authenticated
authenticated = true;
}
}
// Require Basic authentication
if (typeof headers.authorization == 'undefined' || !authenticated) {
const body = 'Unauthorized';
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: body,
headers: {
'www-authenticate': [{ key: 'WWW-Authenticate', value: 'Basic' }]
},
};
callback(null, response);
}
// Continue request processing if authentication passed
callback(null, request);
};
With one single username and password, it works just fine :
Example :
exports.handler = (event, context, callback) => {
// Get the request and its headers
const request = event.Records[0].cf.request;
const headers = request.headers;
// Specify the username and password to be used
const user = 'admin1';
const pw = 'passadmin1';
// Build a Basic Authentication string
const authString = 'Basic ' + new Buffer(user + ':' + pw).toString('base64');
// Challenge for auth if auth credentials are absent or incorrect
if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
const response = {
status: '401',
statusDescription: 'Unauthorized',
body: 'Unauthorized',
headers: {
'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
},
};
callback(null, response);
}
// User has authenticated
callback(null, request);
};
I'm using nodejs 8 and typescript for this function.
Could anyone please tell me what's wrong with the first function ?
You are checking headers.authorization[0] without checking that it is not undefined:
It should be:
...
if (headers.authorization && headers.authorization[0].value == authString) {
// User has authenticated
authenticated = true;
}
...

Getting a request token from Django OauthToolkit via React-Native

I am trying to optain a token from Django - OauthToolkit but I only get the "unsupported_grant_type" error:
Here is what I have writen in react-native:
async getToken (client_id, client_key, username, password) {
let response = await fetch('https://example.com/o/token/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: JSON.stringify({
'client_id': client_id,
'client_secret': client_key,
'grant_type': 'password',
'username': username,
'password': password,
})
})
let responseJson = await response.json()
var token = responseJson.error <- written to see the error (shoul be responseJson.acces_token)
this.setState({token})
}
other posts mentioned it could be an error in the headers - but it leaves me clueless right now.
After scratching my head and countless google searches, here's how I did it.
allow me to make some assuptions
Assume your backend server works fine, and any endpoints are protected.
Assume when you go to access an endpoint you get the error
"detail": "Authentication credentials were not provided."
Assume you can authenticate/get access token from postman or by sending a POST request to /o/token with the parameters
username
password
client_id
grant_type
With django-oauth-toolkit, it's crucial to send the data/body as
'Content-Type': 'application/x-www-form-urlencoded'
Note: My approach might not be neat so I welcome any constructive criticism/advise
import { AUTH_LOGIN} from 'react-admin';
var _client_id = 'xxxxxxxxx';
var _grant_type = 'password';
export default (type, params) => {
if (type === AUTH_LOGIN) {
const {username, password } = params;
let _data = "grant_type="+_grant_type+"&username="+username+"&password="+password+"&client_id="+_client_id
const request = new Request('http://localhost:8000/api/ps/o/oauth/token/', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',}),
body : _data,
})
return fetch(request)
.then(response => {
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ access_token }) => {
localStorage.setItem('token', access_token);
});
}
return Promise.resolve();
}
Finally yet importantly, /o/token returns a dictionary with key 'access_token' and not token. As such, modify your code as highlighted below
.then(({ access_token }) => {localStorage.setItem('token', access_token);});