How to get body of response requested with authentication? - reqwest

When I try to get data from my elasticsearch API the following error appears:
thread 'main' panicked at 'failed to get response: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("localhost")), port: Some(9200), path: "/part_of_speech", query: None, fragment: None }, source: hyper::Error(Connect, Error { code: -67843, message: "The certificate was not trusted." }) }', src/main.rs:15:10
No, not a duplicate of How to get body of response with reqwest? since that one is not using authentication and the solution given did not work.
The following is the code from which I expect the JSON output of the API
#[tokio::main]
async fn main() {
let health = get_health().await;
}
async fn get_health() {
let client = Client::new();
let res = client
.get("https://localhost:9200/speaker")
.basic_auth("elastic", Some("7*7C68392TXAENxKRot"))
.send()
.await
.expect("failed to get response")
.text()
.await
.expect("failed to get payload");
println!("{}", res);
}
What I have tried:
I added the method headers but the same error appeared
.headers(construct_headers())
fn construct_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, HeaderValue::from_static("Basic ZWxhc3RpYzo3KjdDYVJUcGliVFhBRU54S1JvdA=="));
headers
}

Related

Why am I getting a 400 Bad request when calling Plaid's linkTokenCreate function?

I am attempting to set up Plaid in my app, and am following the Quickstart guide for Node.js and Express. When I call the client.linkTokenCreate function I am getting a status 400 Bad Request response. I believe my code exactly matches the quickstart, and I am using sandbox mode, so I am unsure where I am going wrong.
const { Configuration, PlaidApi, PlaidEnvironments, Products, CountryCode } = require("plaid");
const configuration = new Configuration({
basePath: PlaidEnvironments[process.env.PLAID_ENV],
baseOptions: {
headers: {
"PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID,
"PLAID-SECRET": process.env.PLAID_SECRET,
},
},
});
console.log(configuration)
const client = new PlaidApi(configuration);
router.post("/create_link_token", async (req, res) => {
// Get the client_user_id by searching for the current user
// const user = await User.find(...);
// const clientUserId = user.id;
const request = {
user: {
// This should correspond to a unique id for the current user.
client_user_id: "test123",
},
client_name: "Name Of App",
products: [Products.Auth],
language: "en",
webhook: 'https://app.com',
country_codes: [CountryCode.Us],
};
try {
console.log("request",process.env.PLAID_CLIENT_ID,process.env.PLAID_SECRET)
const createTokenResponse = await client.linkTokenCreate(request);
console.log("createTokenResponse", createTokenResponse);
res.status(200).json(createTokenResponse);
} catch (error) {
console.log("error", error.message)
res.send(error.message)
}
});

Get headers with executeOperation on Apollo Server (apollo-server) for integrations tests

Since the deprecation of apollo-server-testing I am using the new way of doing integration tests with apollo-server (included in apollo-server 2.25.0). From the mutation signin I set my refresh token in the OutgoingMessage header's (in 'Set-Cookie').
Simplified resolver
#Mutation(() => RefreshTokenOutput)
async refreshToken(#Ctx() { response, contextRefreshToken }: Context): Promise<RefreshTokenOutput> {
if (contextRefreshToken) {
const { accessToken, refreshToken } = await this.authService.refreshToken(contextRefreshToken);
response.setHeader(
'Set-Cookie',
cookie.serialize('refreshToken', refreshToken, {
httpOnly: true,
maxAge: maxAge,
secure: true,
})
);
return { accessToken: accessToken };
} else {
throw new AuthenticationError();
}
}
Test case
// given:
const { user, clearPassword } = await userLoader.createUser16c();
const input = new UserSigninInput();
input.email = user.email;
input.password = clearPassword;
const MUTATE_signin = gql`
mutation signin($userInput: UserSigninInput!) {
signin(input: $userInput) {
accessToken
}
}
`;
// when:
const res = await server.executeOperation(
{ query: MUTATE_signin, variables: { userInput: input }, operationName: 'signin' },
buildContext(user)
);
I'm trying to test if this token is correctly set and well formed. Did you have any idea on how I can access this header with executeOperation ?
I was able to set headers like this:
const res = await apolloServer.executeOperation({ query: chicken, variables: { id: 1 } }, {req: {headers: 'Authorization sdf'}});
server.executeOperation calls processGraphQLRequest
and processGraphQLRequest return type is GraphQLResponse
export interface GraphQLResponse {
data?: Record<string, any> | null;
errors?: ReadonlyArray<GraphQLFormattedError>;
extensions?: Record<string, any>;
http?: Pick<Response, 'headers'> & Partial<Pick<Mutable<Response>, 'status'>>;
}
I'm not sure, but i think headers in GraphQLResponse.http
you can find call structure in github repo.
https://github.com/apollographql/apollo-server/blob/6b9c2a0f1932e6d8fb94a8662cc1da24980aec6f/packages/apollo-server-core/src/requestPipeline.ts#L126
Apollo defines executeOperation as:
public async executeOperation(
request: Omit<GraphQLRequest, 'query'> & {
query?: string | DocumentNode;
},
integrationContextArgument?: ContextFunctionParams,
) {
integrationContextArgument is optional and ContextFunctionParams is just an alias to any.
As mentioned in an answer above, any context JSON passed to the executeOperation function will be sent to Apollo's processGraphQLRequest() function
graphQLServerOptions() function processes that JSON.
For more advanced scenarios, it seems that a context resolver function, not just JSON context data, can be passed in using the context field
Reference: https://github.com/apollographql/apollo-server/blob/e9ae0f28d11d2fdfc5abd5048c85acf70de21592/packages/apollo-server-core/src/ApolloServer.ts#L1014

Cognito Post Confirmation Trigger adminAddUserToGroup: "Invalid lambda function output : Invalid JSON"

I set the following lambda function as the post-confirmation trigger in Cognito. Anyone know why I'm receiving "Invalid lambda function output : Invalid JSON"?
I'm getting log from "console.log("params", params)" in CloudWatch but not from "console.log("inside adminAddUserToGroup", params)".
const AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
const lambda = new AWS.Lambda();
type TriggerEvent = {
version: string,
region: string,
userPoolId: string,
userName: string,
callerContext: {
awsSdkVersion: string,
clientId: string
},
triggerSource: string,
request: {
userAttributes: {
sub: string,
'cognito:email_alias': string,
'cognito:user_status': string,
birthdate: string,
email_verified: string,
gender: string,
preferred_username: string,
email: string
}
},
response: {}
}
exports.handler = async (event:TriggerEvent, context: any, callback: any) => {
var cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
console.log("event", event);
var params = {
GroupName: 'customers',
UserPoolId: event.userPoolId,
Username: event.userName
};
console.log("params", params)
try {
cognitoIdentityServiceProvider.adminAddUserToGroup(params, function(err: any, data: any) {
console.log("inside adminAddUserToGroup", params)
if (err) {
console.log(err, err.Stack);
context.done(err);
}
context.done();
});
} catch (err) {
console.log("Add user to group catch err", err);
context.fail("Add user to group catch err");
}
}
Thanks.
There are two different problems in the code:
Fixing the missing output of console.log("inside adminAddUserToGroup", params):
You should await on the adminAddUserToGroup(..).promise(), otherwise the lambda will proceed and complete before the adminAddUserToGroup fully executes. You can check this by adding a console.log("Lambda completed") after the try/catch block and see that it does show.
Fix it with:
await cognitoIdentityServiceProvider.adminAddUserToGroup(params, function(err: any, data: any) {
console.log("inside adminAddUserToGroup", params)
if (err) {
console.log(err, err.Stack);
context.done(err);
}
context.done();
}).promise();
Fixing the "Invalid lambda function output : Invalid JSON" response:
You need to return the event, usage of context.done() is deprecated and, since you are using an async handler, you should use return or throw, for success and failure respectively, as per the AWS documentation on Async handler for Typescript.
Fix it by replacing context.done(); with return event;.

Apollo Client 3: query returns results in Chrome's Network tab, but undefined in my app

I am fetching some data using #apollo/client v3. In Chrome's network tab (http results), I can see it returns data and errors (I am not worried about the error, I know why it is right now.):
{
data: {workItems: [,…]},…},
errors: [{message: "Error trying to resolve position."
}
However in my app, data returns undefined.
Here's my client config:
export const graphqlClient = new ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(error =>
console.log(
`[GraphQL error]: ${JSON.stringify(error, null, 2)}`
)
)
}
if (networkError) {
console.log(`[Network error]: ${networkError}`)
}
}),
apolloLink
])
})
And my query:
gql`
query WorkItems($ppm: String) {
workItems(where: { ppm: $ppm }) {
...WorkItemKanban
}
}
${workItemFragment.workItemKanban}
`
const useWorkItemListDataGraphql = (args: {
query: DocumentNode
variables: { parent: string } | { ppm: string }
}) => {
const { variables, query } = args
const { data, error, loading, refetch } = useQuery<
{ workItems: WorkItem[] },
{ parent: string } | { ppm: string }
>(query, {
pollInterval: 180000,
variables
})
// data returns undefined, but error shows the same error as in Chrome's network tab
return { ...data, error, loading, refetch }
}
I am not sure where to start to identify what goes wrong. Loading the data works when there is no error, but I reckon this is not normal behavior, it should always load the same as I can see in Chrome's tab.
As explained in Apollo documentation - Error policies
By default, the error policy treats any GraphQL Errors as network errors and ends the request chain
hence by default, Apollo client returns undefined if there is an error.
Adding errorPolicy: 'all' in the query's options, or in the client default options solves the problem.
Example:
const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: 'all' });

Stitching secure subscriptions using makeRemoteExecutableSchema

We have implemented schema stitching where GraphQL server fetches schema from two remote servers and stitches them together. Everything was working fine when we were only working with Query and Mutations, but now we have a use-case where we even need to stitch Subscriptions and remote schema has auth implemented over it.
We are having a hard time figuring out on how to pass authorization token received in connectionParams from client to remote server via the gateway.
This is how we are introspecting schema:
API Gateway code:
const getLink = async(): Promise<ApolloLink> => {
const http = new HttpLink({uri: process.env.GRAPHQL_ENDPOINT, fetch:fetch})
const link = setContext((request, previousContext) => {
if (previousContext
&& previousContext.graphqlContext
&& previousContext.graphqlContext.request
&& previousContext.graphqlContext.request.headers
&& previousContext.graphqlContext.request.headers.authorization) {
const authorization = previousContext.graphqlContext.request.headers.authorization;
return {
headers: {
authorization
}
}
}
else {
return {};
}
}).concat(http);
const wsLink: any = new WebSocketLink(new SubscriptionClient(process.env.REMOTE_GRAPHQL_WS_ENDPOINT, {
reconnect: true,
// There is no way to update connectionParams dynamically without resetting connection
// connectionParams: () => {
// return { Authorization: wsAuthorization }
// }
}, ws));
// Following does not work
const wsLinkContext = setContext((request, previousContext) => {
let authToken = previousContext.graphqlContext.connection && previousContext.graphqlContext.connection.context ? previousContext.graphqlContext.connection.context.Authorization : null
return {
context: {
Authorization: authToken
}
}
}).concat(<any>wsLink);
const url = split(({query}) => {
const {kind, operation} = <any>getMainDefinition(<any>query);
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLinkContext,
link)
return url;
}
const getSchema = async (): Promise < GraphQLSchema > => {
const link = await getLink();
return makeRemoteExecutableSchema({
schema: await introspectSchema(link),
link,
});
}
const linkSchema = `
extend type UserPayload {
user: User
}
`;
const schema: any = mergeSchemas({
schemas: [linkSchema, getSchema],
});
const server = new GraphQLServer({
schema: schema,
context: req => ({
...req,
})
});
Is there any way for achieving this using graphql-tools? Any help appreciated.
I have one working solution: the idea is to not create one instance of SubscriptionClient for the whole application. Instead, I'm creating the clients for each connection to the proxy server:
server.start({
port: 4000,
subscriptions: {
onConnect: (connectionParams, websocket, context) => {
return {
subscriptionClients: {
messageService: new SubscriptionClient(process.env.MESSAGE_SERVICE_SUBSCRIPTION_URL, {
connectionParams,
reconnect: true,
}, ws)
}
};
},
onDisconnect: async (websocket, context) => {
const params = await context.initPromise;
const { subscriptionClients } = params;
for (const key in subscriptionClients) {
subscriptionClients[key].close();
}
}
}
}, (options) => console.log('Server is running on http://localhost:4000'))
if you would have more remote schemas you would just create more instances of SubscriptionClient in the subscriptionClients map.
To use those clients in the remote schema you need to do two things:
expose them in the context:
const server = new GraphQLServer({
schema,
context: ({ connection }) => {
if (connection && connection.context) {
return connection.context;
}
}
});
use custom link implementation instead of WsLink
(operation, forward) => {
const context = operation.getContext();
const { graphqlContext: { subscriptionClients } } = context;
return subscriptionClients && subscriptionClients[clientName] && subscriptionClients[clientName].request(operation);
};
In this way, the whole connection params will be passed to the remote server.
The whole example can be found here: https://gist.github.com/josephktcheung/cd1b65b321736a520ae9d822ae5a951b
Disclaimer:
The code is not mine, as #josephktcheung outrun me with providing an example. I just helped with it a little. Here is the original discussion: https://github.com/apollographql/graphql-tools/issues/864
This is a working example of remote schema with subscription by webscoket and query and mutation by http. It can be secured by custom headers(params) and shown in this example.
Flow
Client request
-> context is created by reading req or connection(jwt is decoded and create user object in the context)
-> remote schema is executed
-> link is called
-> link is splitted by operation(wsLink for subscription, httpLink for queries and mutations)
-> wsLink or httpLink access to context created above (=graphqlContext)
-> wsLink or httpLink use context to created headers(authorization header with signed jwt in this example) for remote schema.
-> "subscription" or "query or mutation" are forwarded to remote server.
Note
Currently, ContextLink does not have any effect on WebsocketLink. So, instead of concat, we should create raw ApolloLink.
When creating context, checkout connection, not only req. The former will be available if the request is websocket, and it contains meta information user sends, like an auth token.
HttpLink expects global fetch with standard spec. Thus, do not use node-fetch, whose spec is incompatible (especially with typescript). Instead, use cross-fetch.
const wsLink = new ApolloLink(operation => {
// This is your context!
const context = operation.getContext().graphqlContext
// Create a new websocket link per request
return new WebSocketLink({
uri: "<YOUR_URI>",
options: {
reconnect: true,
connectionParams: { // give custom params to your websocket backend (e.g. to handle auth)
headers: {
authorization: jwt.sign(context.user, process.env.SUPER_SECRET),
foo: 'bar'
}
},
},
webSocketImpl: ws,
}).request(operation)
// Instead of using `forward()` of Apollo link, we directly use websocketLink's request method
})
const httpLink = setContext((_graphqlRequest, { graphqlContext }) => {
return {
headers: {
authorization: jwt.sign(graphqlContext.user, process.env.SUPER_SECRET),
},
}
}).concat(new HttpLink({
uri,
fetch,
}))
const link = split(
operation => {
const definition = getMainDefinition(operation.query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink, // <-- Executed if above function returns true
httpLink, // <-- Executed if above function returns false
)
const schema = await introspectSchema(link)
const executableSchema = makeRemoteExecutableSchema({
schema,
link,
})
const server = new ApolloServer({
schema: mergeSchemas([ executableSchema, /* ...anotherschemas */]),
context: ({ req, connection }) => {
let authorization;
if (req) { // when query or mutation is requested by http
authorization = req.headers.authorization
} else if (connection) { // when subscription is requested by websocket
authorization = connection.context.authorization
}
const token = authorization.replace('Bearer ', '')
return {
user: getUserFromToken(token),
}
},
})

Resources