FeathersJS Twitch OAuth 401 Unauthorized - javascript

I'm new to FeathersJS. I tried to set up OAuth login with Twitch. I created a twitch oauth application and did the same as here with github login. I want to save the user in my MongoDB database, but I'm getting redirected to http://localhost:3030/#error=401%20Unauthorized after logging in on twitch. I've generated a fresh application with the feathers cli. What am I doing wrong?
config/default.json
...
"oauth": {
"redirect": "/",
"twitch": {
"key": ""*****************",",
"secret": ""***************************************"",
"scope": ["user:read:email"]
},
"github": {
"key": "*****************",
"secret": "***************************************"
}
}...

This is because the built-in strategy is attempting to fetch the user profile, yet it is not including your Client ID in the headers (see Twitch API - Getting Started.
To solve this you need to create your own strategy that implements getProfile. I used the Facebook demo from the feathersjs cookbook as a reference which can be found here.
Here is my implementation:
./strategies/TwitchStrategy.ts
import { Params } from '#feathersjs/feathers'
import { AuthenticationRequest } from '#feathersjs/authentication'
import { OAuthStrategy, OAuthProfile } from '#feathersjs/authentication-oauth'
import axios from 'axios'
import { Application } from '../declarations'
export class TwitchStrategy extends OAuthStrategy {
// we need a reference to the app instance
app: Application = {} as Application
// when the strategy is initialized this method is called with an app instance
setApplication(appInstance: Application): void {
this.app = appInstance
}
// this method is used to get the user profile after they authorize with the provider
async getProfile(authResult: AuthenticationRequest, _params: Params) {
const accessToken = authResult.access_token
const { data } = await axios.get('https://api.twitch.tv/helix/users', {
headers: {
Authorization: `Bearer ${accessToken}`, //our users access token to look them up
'Client-ID': this.app.get('authentication').oauth.twitch.key //we need to send the Client-ID
},
params: {
fields: 'id,name,email'
}
})
console.log(data)
return data
}
async getEntityData(profile: OAuthProfile, existing: any, params: Params) {
// `profile` is the data returned by getProfile
const baseData = await super.getEntityData(profile, existing, params)
return {
...baseData,
email: profile.email
}
}
}
./authentication.ts
import { ServiceAddons } from '#feathersjs/feathers'
import { AuthenticationService, JWTStrategy } from '#feathersjs/authentication'
import { LocalStrategy } from '#feathersjs/authentication-local'
// import our strategy
import { TwitchStrategy } from './strategies/TwitchStrategy'
import { expressOauth } from '#feathersjs/authentication-oauth'
import { Application } from './declarations'
declare module './declarations' {
interface ServiceTypes {
authentication: AuthenticationService & ServiceAddons<any>
}
}
export default function (app: Application): void {
const authentication = new AuthenticationService(app)
authentication.register('jwt', new JWTStrategy())
// register our custom strategy
authentication.register('twitch', new TwitchStrategy())
authentication.register('local', new LocalStrategy())
app.use('/authentication', authentication)
app.configure(expressOauth())
}

Related

BrowserAuthError: interaction_in_progress when trying to get accesstoken in React

I am trying to used Azure AD to integrate my application, but I keep getting this error
AuthError.ts:49 Uncaught (in promise) BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API. For more visit: aka.ms/msaljs/browser-errors.
at BrowserAuthError.AuthError [as constructor] (AuthError.ts:49:1)
at new BrowserAuthError (BrowserAuthError.ts:195:1)
at BrowserAuthError.createInteractionInProgressError (BrowserAuthError.ts:276:1)
at BrowserCacheManager.setInteractionInProgress (BrowserCacheManager.ts:1000:1)
at ClientApplication.preflightInteractiveRequest (ClientApplication.ts:837:1)
at ClientApplication.preflightBrowserEnvironmentCheck (ClientApplication.ts:820:1)
at PublicClientApplication.<anonymous> (ClientApplication.ts:272:1)
at step (vendors~main.chunk.js:217:19)
at Object.next (vendors~main.chunk.js:147:14)
at vendors~main.chunk.js:119:67
at new Promise (<anonymous>)
at __awaiter (vendors~main.chunk.js:98:10)
at ClientApplication.acquireTokenRedirect (ClientApplication.ts:268:1)
at index.tsx:50:1
This is my index.tsx file:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import '#scuf/common/honeywell/theme.css';
import '#scuf/datatable/honeywell/theme.css';
import store from './stores';
import { Provider } from 'mobx-react';
import createRouter from './router';
import './index.scss';
import { msalConfig } from "./stores/authConfig";
import { MsalProvider, MsalAuthenticationTemplate } from "#azure/msal-react";
import { InteractionRequiredAuthError, AuthError } from "#azure/msal-common";
import { PublicClientApplication, InteractionType } from "#azure/msal-browser";
const msalInstance = new PublicClientApplication(msalConfig);
msalInstance.handleRedirectPromise()
.then((redirectResponse) => {
if (redirectResponse !== null) {
// Acquire token silent success
let accessToken = redirectResponse.accessToken;
console.log(accessToken)
// Call your API with token
} else {
// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const activeAccount = msalInstance.getActiveAccount();
const accounts = msalInstance.getAllAccounts();
if (!activeAccount && accounts.length === 0) {
console.error("User not logged in!!!");
}
const accessTokenRequest = {
scopes: ["user.read", "openid"],
account: activeAccount || accounts[0],
// roles: ["rca.approver"],
};
msalInstance
.acquireTokenSilent(accessTokenRequest)
.then(function (accessTokenResponse) {
// Acquire token silent success
// Call API with token
let accessToken = accessTokenResponse.accessToken;
console.log(accessToken)
// Call your API with token
})
.catch(function (error) {
//Acquire token silent failure, and send an interactive request
console.log(error);
if (error instanceof InteractionRequiredAuthError || error instanceof AuthError) {
msalInstance.acquireTokenRedirect(accessTokenRequest);
}
});
}
})
// Here we are importing our stores file and spreading it across this Provider. All stores added to this will be accessible via child injects
const wrappedApp = (
<MsalProvider instance={msalInstance}>
<MsalAuthenticationTemplate interactionType={InteractionType.Redirect}>
<Provider store={store}>
<App />
</Provider>
</MsalAuthenticationTemplate>
</MsalProvider>
);
// Here the router is bootstrapped
const router = createRouter();
router.start(() => {
ReactDOM.render(wrappedApp, document.getElementById('root') as HTMLElement);
});
and this is my authConfig.js:
export const msalConfig = {
auth: {
clientId: "XXX",
authority: "https://login.microsoftonline.com/YYY",
redirectUri: "http://localhost:3000",
postLogoutRedirectUri: "http://localhost:3000",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
}
};
// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read","openid"],
redirectUri: "http://localhost:3000",
};
// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
I have tried the solution in net but it still gives me the same error. These are the only two files in my project folder that deals with MSAL packages. Did I miss anything? As I learnt from the documenatation, interactionType redirects to AD authentication on which token is generated which could then be sent to APIs. Please correct me if I am wrong.

Create T3 App Redirect inside a TRPC middleware if user is not signed

How can I trigger a redirect on the server side if a signed in user has not completed their profile page
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
// redirect to profile page if user has not completed profile
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
});
});
This is not currently possible in the way you are describing to the best of my knowledge.
Here are some alternatives that might be helpful:
In getServerSideProps
this only works if you want to redirect before the initial page load. You could also create a wrapper around gSSP to make this more DRY if you're going to use it on a lot of pages.
import { type GetServerSidePropsContext } from "next";
import { getServerAuthSession } from "../server/auth";
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const session = await getServerAuthSession(ctx);
if (!session) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
return {
props: {},
};
}
export default function AuthedPage() {
return <div>Authed</div>;
}
As part of a query or mutation clientside
this is useful for a query or mutation that is only fired after the page has loaded. Again this is a very simple example and could be DRYed, probably the easiest way would be to extract into a custom hook.
import { useRouter } from "next/router";
import { api } from "../utils/api";
export default function AuthedPage() {
const router = useRouter();
// `authedHello` is the example Create T3 App "hello" procedure
// but as a protectedProcedure, ie throws "UNAUTHORIZED" if no session.
// Replace this with a middleware that throws on whatever condition you need it to.
const authedHello = api.example.protectedHello.useQuery(
{ text: "world" },
{
retry: (_count, err) => {
// `onError` only runs once React Query stops retrying
if (err.data?.code === "UNAUTHORIZED") {
return false;
}
return true;
},
onError: (err) => {
if (err.data?.code === "UNAUTHORIZED") {
void router.push("/");
}
},
}
);
return (
<div>
<h1>Authed Page</h1>
<p>{authedHello.data?.greeting}</p>
</div>
);
}
Using Next.js middleware
This is easy to apply to a bunch of routes using the matcher, but it falls a bit outside of T3 conventions.
// pages/middleware.ts
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "../server/auth";
import type { NextApiRequest, NextApiResponse } from "next";
export async function middleware(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession(req, res, authOptions);
if (!session?.user) {
return NextResponse.redirect(new URL("/", req.url));
}
}
export const config = {
matcher: ["/protectedPage", "/anotherProtectedPage"],
};
Using require in next-auth's useSession
this is useful if you want to guard a page but can't use getServerSideProps. It doesn't quite solve your specific problem, but might be useful to other people who find this. See: https://next-auth.js.org/getting-started/client#require-session

NestJS Permissions Guard - the most efficient way

Currently, I'm working on NestJS API. I'd like to prepare Permissions Guard and I have a problem with this. Users can have only one role, one role can have a lot of permissions. Permissions for roles are set on the Admin panel, so role permissions can be often changed. I cannot understand how can I deal with permissions in PermissionGuard. I know that I can check the current state of them in the database, but I think it's not the best way to do that because the database will be queried too often.
What should I do? Any idea?
Works nice. It's a JwtAuthGuard improvement and checking one permission.
import { CanActivate, ExecutionContext, Type, mixin } from '#nestjs/common';
import { EPermission } from '../path-with-your-enum-values';
import { JWTRequestPayload } from '../request-payload-type';
import { JwtAuthGuard } from './jwt-auth.guard';
export const PermissionGuard = (permission: EPermission): Type<CanActivate> => {
class PermissionGuardMixin extends JwtAuthGuard {
async canActivate(context: ExecutionContext) {
await super.canActivate(context);
const request = context.switchToHttp().getRequest<JWTRequestPayload>();
const user = request.user;
if (!user || !user.permissions) {
return false;
}
return user.permissions.includes(permission);
}
}
return mixin(PermissionGuardMixin);
};
And with controller:
#Post(':taskId/moderate')
#UseGuards(PermissionGuard(EPermission.MODERATE))
public async moderate(#Param('taskId') taskId: string): Promise<any> {
// ...
}

vue-apollo 3.0.0 Beta configuration

Pretty new at this so any help much appreciated.
I know how to do Authentication with Apollo client but when I add to my Vue-cli-3 generated project the new vue-apollo-plugin (https://www.npmjs.com/package/vue-apollo). I don't understand how and where to configure my authMiddleware.
Here is the auto generated file form the the cli:
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
// Install the vue plugin
Vue.use(VueApollo)
// Name of the localStorage item
const AUTH_TOKEN = 'apollo-token'
// Config
const defaultOptions = {
httpEndpoint: process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:4000', // Use `null` to disable subscriptions
wsEndpoint: process.env.VUE_APP_GRAPHQL_WS || 'ws://localhost:4000',
// LocalStorage token
tokenName: AUTH_TOKEN,
// Enable Automatic Query persisting with Apollo Engine
persisting: false,
// Use websockets for everything (no HTTP)
// You need to pass a `wsEndpoint` for this to work
websocketsOnly: false,
// Is being rendered on the server?
ssr: false,
// Additional ApolloClient options
// apollo: { ... }
// Client local data (see apollo-link-state)
// clientState: { resolvers: { ... }, defaults: { ... } }
}
// Call this in the Vue app file
export function createProvider (options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options,
})
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
// fetchPolicy: 'cache-and-network',
},
},
errorHandler (error) {
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
},
})
return apolloProvider
}
// Manually call this when user log in
export async function onLogin (apolloClient, token) {
localStorage.setItem(AUTH_TOKEN, token)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (login)', 'color: orange;', e.message)
}
}
// Manually call this when user log out
export async function onLogout (apolloClient) {
localStorage.removeItem(AUTH_TOKEN)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
}
}
I have what I would previously use for authentication via the header here:
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
const token = localStorage.getItem(AUTH_TOKEN)
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : null
}
})
return forward(operation)
})
It seems like when I dig a bit deeper into some of the imported objects from the vue-apollo package there is something like this already built in in the createApolloClient object it has this property:
authLink = setContext(function (_, _ref2) {
var headers = _ref2.headers;
return {
headers: _objectSpread({}, headers, {
authorization: getAuth(tokenName)
})
};
});
Does this mean I can simply destructure the property off the createApolloClient object? Any help or tips much appreciated.
Take a look at vue-cli-plugin-apollo
You can pass a link: authLink and\or getAuth:()=>{return "something"} in const defaultOptions = { ... } in /vue-apollo.js.
Or in main.js when you call createProvider
new Vue({
// router, store
apolloProvider: createProvider({
link: authLink,
getAuth: AUTH_TOKEN => localStorage.getItem(AUTH_TOKEN)
}),
// ...
})
using both if you adding header in authLink, getAuth is probably redundant.
if you plan to use more than one link, there is apollo-link package link: ApolloLink.from([ ... ])

Wrapping a RESTapi with GraphQL using Apollo React

I need to do a project (currency exchange app) using Apollo client and React. I need to wrap an existing REST api (fixer.io) with graphql. So far no luck finding a solution online. Tried several tutorials but they don't seem to work. Anyone have experience with this?
Thanks.
I assume you use Apollo client 2.0 and want everything to be client side.
First you need an apollo bridge link. It's used "When you don't have GraphQL server (yet) and want to use GraphQL on the client". Its source code is quite short, so you can inline it:
/*
Copyright (c) 2017 David Cizek
https://github.com/dacz/apollo-bridge-link
*/
import { GraphQLSchema, graphql, print } from 'graphql';
import { addMockFunctionsToSchema, makeExecutableSchema } from 'graphql-tools';
import { ApolloLink } from 'apollo-link';
import Observable from 'zen-observable';
export const createBridgeLink = ({ schema, resolvers, mock, context = {} }) => {
let executableSchema;
if (typeof schema === 'string') {
executableSchema = makeExecutableSchema({ typeDefs: schema, resolvers });
} else if (schema.kind === 'Document') {
executableSchema = makeExecutableSchema({
typeDefs: print(schema),
resolvers,
});
} else if (schema instanceof GraphQLSchema) {
executableSchema = schema;
} else {
throw new Error('schema should be plain text, parsed schema or executable schema.');
}
if (mock)
{addMockFunctionsToSchema({
schema: executableSchema,
preserveResolvers: true,
});}
return new ApolloLink(
operation =>
new Observable(observer => {
const { headers, credentials } = operation.getContext();
const ctx = {
...context,
headers,
credentials,
};
graphql(executableSchema, print(operation.query), undefined, ctx, operation.variables, operation.operationName)
.then(data => {
observer.next(data);
observer.complete();
})
.catch(err => {
/* istanbul ignore next */
observer.error(err);
});
}),
);
};
export class BridgeLink extends ApolloLink {
requester;
constructor(opts) {
super();
this.requester = createBridgeLink(opts).request;
}
request(op) {
return this.requester(op);
}
}
Next you create schema and resolvers:
// schema.js
export default `
type Rate {
date: Date!
rate: Float!
}
type Query {
latestRate(from: String!, to: String!): Rate
}
schema {
query: Query
}
`;
// resolvers.js
const resolvers = {
Query: {
latestRate(obj, args, context, info) {
return fetch(`https://api.fixer.io/latest?base=${args.from}`).then(res => res.json())
.then(res => { date: res.date, rate: res.rates[args.to] })
}
}
}
export default resolvers;
Finally, you create an apollo client factory:
// clientFactory.js
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { BridgeLink } from './apollo-bridge-link';
import schema from './schema';
import resolvers from './resolvers';
export default () => {
const mock = false;
const context = {};
const client = new ApolloClient({
link: new BridgeLink({ schema, resolvers, mock, context }),
cache: new InMemoryCache(),
});
return client;
};
Here's how you use it:
import gql from 'graphql-tag';
import clientFactory from './clientFactory'
const client = clientFactory();
client.query(gql`query {
latestRate(from: "USD", to: "EUR") { date, rate }
}`).then(console.log)
If you want to use it in React:
import { ApolloProvider } from 'react-apollo';
const client = clientFactory();
const App = ({ data: { latestRate, refetch } }) => {
return <div>
<span>Today:</span><span>{latestRate.date}</span>
<span>1 USD equals:</span><span>{latestRate.rate} EUR</span>
<button onClick={() => refetch()}>
Refresh
</button>
</div>
}
const AppWithQuery = graphql(gql`
query {
latestRate(from: "USD", to: "EUR") { date, rate }
}
`)(App);
ReactDOM.render(
<ApolloProvider client={client}>
<AppWithQuery/>
</ApolloProvider>,
document.getElementById('root'),
);
With the Graphcool Framework, you can define resolver functions, which allow you to easily wrap any REST API. You can define a function and connect it to a specific mutation or query in your GraphQL Schema.
I prepared a demo, wrapping the fixer API.
Try to run this query to get the exchange rates with USD as base, for example:
query {
fixer(
base: "USD"
) {
base
date
eur
usd
rub
}
}
You can build this demo yourself like this:
git clone git#github.com:graphcool/templates.git
cd templates/curated/misc/fixer-wrapper
npm install -g graphcool#next
graphcool init
graphcool deploy
graphcool playground
Please feel free to share any improvement you might have in mind, the example is open source. You can read more about resolvers here.

Categories

Resources