Keycloak - login in Pop Up window - keycloak

I am using keycloak in an iframe. My keycloak client is Okta.
With my current configuration, keycloak simply redirects to okta login page. This means they are beeing opened within the iframe aswell. I'd really like to change that, since it contradicts some major security policies I have.
Can anyone tell how to redirect keycloak to open okta login in a pop up winodw using react/javascript.

Here i done :
=========== LoginButton Component ========
function LoginButton(props) {
window.addEventListener('storage',event => {
if (event.key === 'logged-user') {
// update the redux store variable for login successful
}
});
const clickHandler = () => {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const win=open('/login','Login',`width=500,height=600, left=${x},top=${y}`);
};
return (
<>
<Button
onClick={clickHandler}
> Login </Button>
</>
)
}
export default LoginButton
=========== Login Component ========
const LogIn = props => {
const { keycloak } = useKeycloak();
const [isLoginCalled, setIsLoginCalled] = useState(false);
// load the profile and call saga for update login information
function loadUserInfo() {
keycloak
.loadUserProfile()
.then(function(profile) {
localStorage.setItem('logged-user', profile.username);
window.open('', '_self').close();
})
.catch(function(err) {
console.log(`Error while loading progile :${err}`);
});
}
useEffect(() => {
if(keycloak.authenticated && !isLoginCalled) {
loadUserInfo();
setIsLoginCalled(true);
} else {
keycloak.login();
}
}, [1]);
return (
<>
<h1>Loading please wait...</h1>
</>
);
};
export deault LogIn

Related

Ionic React: InAppPurchase2 states "Product does not exist"

I was following the example shown at:
Ionic React: Implementing InAppPurchase 2 on React Hooks
I kept getting errors saying that:
"
InAppPurchase[objc]: Product (signatureyearly) does not exist or is not sucessfully initialized.
"
I have tried "com.myappname.app.signatureyearly" as well but I get similar errors.
I have double confirmed that my app bundle id is "com.myappname.app" and my IAP product ID is "signatureyearly" it is a renewal subscription and it is "Ready to submit".
Really need help with this, has been trying to figure this out for many days.
This is what I have written so far.
Thank you so much !!!!
import React, { useState, useEffect } from 'react';
import { InAppPurchase2 as iap, IAPProduct } from "#ionic-native/in-app-purchase-2";
export const TestStore: React.FC = () => {
//declare variables
const [productPrice, setPrice] = useState('')
const [product, setProduct] = useState([]) as any
//initiate initInAppPurchase function
useEffect(() => {
const init = async () => {
await initInAppPurchase();
}
init();
}, []);
const initInAppPurchase = () => {
iap.verbosity = iap.DEBUG;
iap.register({
id: "signatureyearly",
type: iap.PAID_SUBSCRIPTION
});
iap.ready(() => {
let product = iap.get('signatureyearly');
setPrice(product.price)
setProduct(product)
})
iap.refresh();
}
//if user clicks purchase button
const purchaseProduct = () => {
if (product.owned) {
alert('Product already owned, click restore button instead!')
} else {
iap.order('signatureyearly').then(() => {
iap.when("signatureyearly").approved((p: IAPProduct) => {
//store product
p.verify();
p.finish();
});
})
iap.refresh();
}
}
//if user clicks retore or promo code button
const restore = () => {
iap.when("signatureyearly").owned((p: IAPProduct) => {
if (product.owned) {
//store product
} else {
alert("You have not purchased this product before.")
}
});
iap.refresh();
}
return (
<div>
<button onClick={purchaseProduct}>TEST 4 :Buy for {productPrice}</button>
<button onClick={restore}>Restore</button>
<button onClick={restore}>Promo code</button>
</div>
);
};

Is there a way to detect server side cookie for all pages in nextjs? [duplicate]

So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.
The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.
This is my HOC code:
(cookie are stored under userToken name)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.
You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.
For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
You would then use it in your page by wrapping the getServerSideProps function.
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
Based on Julio's answer, I made it work for iron-session:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '#/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
And then I use it like:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
My withSessionSsr function looks like:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }

keycloak-js client in PWA not using cached token from storage

I would like to authenticate my react app - PWA based on create-react-app with keycloak server, and once user is authenticated then i would like them to be able to browser side offline with valid token that i have cached in localStorage but even when passed tokens to keycloak init, i can see user redirected to keycloak auth server.
library version:-
"keycloak-js": "^12.0.2",
"#react-keycloak/web": "^3.4.0",
App.tsx
class App extends React.Component {
tokens: any;
constructor(props: any) {
super(props);
this.tokens = JSON.parse(localStorage.getItem(kcTokens) || '{}');
}
onTokens = (tokens: Pick<AuthClient, "idToken" | "refreshToken" | "token">) => {
localStorage.setItem(kcTokens, JSON.stringify(tokens));
}
onEvent = (event: AuthClientEvent, error?: AuthClientError | undefined) => {
console.log('onKeycloakEvent', event, error);
}
render() {
return (
<ReactKeycloakProvider
authClient={keycloak}
initOptions={{
onLoad: 'check-sso',
...this.tokens
}}
LoadingComponent={loadingComponent}
onEvent={this.onEvent}
onTokens={this.onTokens}
>
<AppRouter />
</ReactKeycloakProvider>
)
}
}
AppRouter.tsx
const AppRouter = () => {
return (
<Router history={history}>
<Switch>
<PrivateRoute path="/" exact component={Home}></PrivateRoute>
</Switch>
</Router>
)
}
PrivateRoute.tsx
const PrivateRoute : React.FC<PrivateRouteProps> = ({ component: Component, ...rest }) => {
const { keycloak } = useKeycloak();
React.useEffect(() => {
if (!keycloak?.authenticated) {
keycloak.login();
}
}, [keycloak]);
return (
<Route
{...rest}
render={props => (
keycloak?.authenticated && <Component {...props} />
)}
/>
);
}

Redirect to requested page after login using vue-router

In my application some routes are just accessible for authenticated users.When a unauthenticated user clicks on a link, for which he has to be signed in, he will be redirected to the login component.
If the user logs in successfully, I would like to redirect him to the URL he requested before he had to log in. However, there also should be a default route, in case the user did not request another URL before he logged in.
How can I achieve this using vue-router?
My code without redirect after login
router.beforeEach(
(to, from, next) => {
if(to.matched.some(record => record.meta.forVisitors)) {
next()
} else if(to.matched.some(record => record.meta.forAuth)) {
if(!Vue.auth.isAuthenticated()) {
next({
path: '/login'
// Redirect to original path if specified
})
} else {
next()
}
} else {
next()
}
}
)
My login function in my login component
login() {
var data = {
client_id: 2,
client_secret: '**************',
grant_type: 'password',
username: this.email,
password: this.password
}
// send data
this.$http.post('oauth/token', data)
.then(response => {
// authenticate the user
this.$auth.setToken(response.body.access_token,
response.body.expires_in + Date.now())
// redirect to route after successful login
this.$router.push('/')
})
}
This can be achieved by adding the redirect path in the route as a query parameter.
Then when you login, you have to check if the redirect parameter is set:
if IS set redirect to the path found in param
if is NOT set you can fallback on root.
Put an action to your link for example:
onLinkClicked() {
if(!isAuthenticated) {
// If not authenticated, add a path where to redirect after login.
this.$router.push({ name: 'login', query: { redirect: '/path' } });
}
}
The login submit action:
submitForm() {
AuthService.login(this.credentials)
.then(() => this.$router.push(this.$route.query.redirect || '/'))
.catch(error => { /*handle errors*/ })
}
I know this is old but it's the first result in google and for those of you that just want it given to you this is what you add to your two files. In my case I am using firebase for auth.
Router
The key line here is const loginpath = window.location.pathname; where I get the relative path of their first visit and then the next line next({ name: 'Login', query: { from: loginpath } }); I pass as a query in the redirect.
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
if (requiresAuth && !currentUser) {
const loginpath = window.location.pathname;
next({ name: 'Login', query: { from: loginpath } });
} else if (!requiresAuth && currentUser) next('menu');
else next();
});
Login Page
No magic here you'll just notice my action upon the user being authenticated this.$router.replace(this.$route.query.from); it sends them to the query url we generated earlier.
signIn() {
firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(
(user) => {
this.$router.replace(this.$route.query.from);
},
(err) => {
this.loginerr = err.message;
},
);
},
I am going to be fleshing out this logic in more detail but it works as is. I hope this helps those that come across this page.
Following on from Matt C's answer, this is probably the simplest solution but there were a few issues with that post, so I thought it best to write a complete solution.
The destination route can be stored in the browser's session storage and retrieved after authentication. The benefit of using session storage over using local storage in this case is that the data doesn't linger after a broswer session is ended.
In the router's beforeEach hook set the destination path in session storage so that it can be retrieved after authentication. This works also if you are redirected via a third party auth provider (Google, Facebook etc).
router.js
// If user is not authenticated, before redirecting to login in beforeEach
sessionStorage.setItem('redirectPath', to.path)
So a fuller example might look something like this. I'm using Firebase here but if you're not you can modify it for your purposes:
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(x => x.meta.requiresAuth);
const currentUser = firebase.auth().currentUser;
if (requiresAuth && !currentUser) {
sessionStorage.setItem('redirectPath', to.path);
next('/login');
} else if (requiresAuth && currentUser) {
next();
} else {
next();
}
});
login.vue
In your login method, after authetication you will have a line of code that will send the user to a different route. This line will now read the value from session storage. Afterwards we will delete the item from session storage so that it is not accidently used in future (if you the user went directly to the login page on next auth for instance).
this.$router.replace(sessionStorage.getItem('redirectPath') || '/defaultpath');
sessionStorage.removeItem('redirectPath');
A fuller example might look like this:
export default Vue.extend({
name: 'Login',
data() {
return {
loginForm: {
email: '',
password: ''
}
}
},
methods: {
login() {
auth.signInWithEmailAndPassword(this.loginForm.email, this.loginForm.password).then(user => {
//Go to '/defaultpath' if no redirectPath value is set
this.$router.replace(sessionStorage.getItem('redirectPath') || '/defaultpath');
//Cleanup redirectPath
sessionStorage.removeItem('redirectPath');
}).catch(err => {
console.log(err);
});
},
},
});
If route guard is setup as below
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!loggedIn) {
next({
path: "/login",
query: { redirect: to.fullPath }
});
} else {
next();
}
} else {
next();
}
});
The redirect query can be extracted and used upon successful login
let searchParams = new URLSearchParams(window.location.search);
if (searchParams.has("redirect")) {
this.$router.push({ path: `${searchParams.get("redirect")}` });
} else this.$router.push({ path: "/dashboard" });
Another quick and dirty option would be to use local storage like the following:
In your beforeEach, before you redirect to login place the following line of code to save the initial requested path to local storage:
router.js
// If user is not authenticated, before redirecting to login
localStorage.setItem('pathToLoadAfterLogin', to.path)
Then in your login component, upon succesful login, you can redirect to the localStorage variable that you previously created:
login.vue
// If user login is successful, route them to what they previously requested or some default route this.$router.push(localStorage.getItem('pathToLoadAfterLogin') || 'somedefaultroute');
Much easier with this library
and login function is
let redirect = this.$auth.redirect();
this.$auth
.login({
data: this.model,
rememberMe: true,
redirect: { name: redirect ? redirect.from.name : "homepage", query: redirect.from.query },
fetchUser: true
})
This will help you #Schwesi .
Router.beforeEach(
(to, from, next) => {
if (to.matched.some(record => record.meta.forVisitors)) {
if (Vue.auth.isAuthenticated()) {
next({
path: '/feed'
})
} else
next()
}
else if (to.matched.some(record => record.meta.forAuth)) {
if (!Vue.auth.isAuthenticated()) {
next({
path: '/login'
})
} else
next()
} else
next()
}
);
This worked for me.
this.axios.post('your api link', {
token: this.token,
})
.then(() => this.$router.push(this.$route.query.redirect || '/dashboard'))
In Vue2 if someone has a routing and guarded some groups of routes. I solved this way.
function webGuard(to, from, next) {
if (!store.getters["auth/authenticated"]) {
sessionStorage.setItem("redirect", to); // hear I save the to
next("/login");
} else {
next();
}
}
Vue.use(VueRouter);
export default new VueRouter({
mode: "history",
hash: false,
routes: [
{
path: "/",
component: Home,
children: [
{ path: "", redirect: "home" },
...
...
],
beforeEnter: webGuard
},]
when you login
this.signIn({ email: test#gmail.com, password: 123 })
.then((res) => {
var redirectPath = sessionStorage.getItem('redirect');
sessionStorage.removeItem('redirect');
this.$router.push(redirectPath?redirectPath:"/dashboard");
})

Vue.js 2 and auth0 authentication resulting with 'nonce'

I am trying to implement auth0 in my Vue.js 2 application.
I followed this link to implement the auth0 lock:
https://github.com/auth0-samples/auth0-vue-samples/tree/master/01-Login
This is my application in Login.vue:
HTML:
<div v-show="authenticated">
<button #click="logout()">Logout</button>
</div>
<div v-show="!authenticated">
<button #click="login()">Login</button>
</div>
Javascript:
function checkAuth() {
return !!localStorage.getItem('id_token');
}
export default {
name: 'login',
data() {
return {
localStorage,
authenticated: false,
secretThing: '',
lock: new Auth0Lock('clientId', 'domain')
}
},
events: {
'logout': function() {
this.logout();
}
},
mounted() {
console.log('mounted');
var self = this;
Vue.nextTick(function() {
self.authenticated = checkAuth();
self.lock.on('authenticated', (authResult) => {
console.log(authResult);
console.log('authenticated');
localStorage.setItem('id_token', authResult.idToken);
self.lock.getProfile(authResult.idToken, (error, profile) => {
if (error) {
console.log(error);
return;
} else {
console.log('no error');
}
localStorage.setItem('profile', JSON.stringify(profile));
self.authenticated = true;
});
});
self.lock.on('authorization_error', (error) => {
console.log(error);
});
});
},
methods: {
login() {
this.lock.show();
},
logout() {
localStorage.removeItem('id_token');
localStorage.removeItem('profile');
this.authenticated = false;
}
}
}
I am pretty sure that it already worked, but suddenly it doesnt work anymore.
My callbacks defined in auth0: http://127.0.0.1:8080/#/backend/login
That is also how I open the login in my browser.
When I login it I only get this in my localStorage:
Key: com.auth0.auth.14BK0_jsJtUZMxjiy~3HBYNg27H4Xyp
Value: {"nonce":"eKGLcD14uEduBS-3MUIQdupDrRWLkKuv"}
I also get redirected to http://127.0.0.1:8080/#/ so I do not see any network requests.
Does someone know where the problem is?
I ran the demo from auth0 with my Domain/Client and it worked without any problem.
Obviously I do not get any errors back in my console.
Atfer research I finally found the answer to my problem.
The reason, why it is not working is because my vue-router does not use the HTML5 History Mode (http://router.vuejs.org/en/essentials/history-mode.html).
To have it working without the history mode, I had to disable the redirect in my lock options and to disable auto parsing the hash:
lock: new Auth0Lock(
'clientId',
'domain', {
auth: {
autoParseHash: false,
redirect: false
}
}
)
Reference: https://github.com/auth0/lock