I'm trying to show and hide links (black boxes and circle) based on the onAuthStateChanged from Firebase with SvelteKit. The sign-in & logout functionality work based on console.log() but whenever I use SvelteKit's {If} with {#if user} to check for a user nothing changes.
Page Structure:
The Code:
signIn.svelte(Main Login Function)
function login() {
let email = document.getElementById("emailInput").value;
let password = document.getElementById("passInput").value;
if (title == "Login") {
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
localStorage.setItem("uid", user.uid);
localStorage.setItem("isLoggedIn", true);
goto("/");
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
} else {
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
console.log(user);
goto("/");
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
}
}
Navbar.svelte
Navbar , NavUl , and NavLi are from the flowbite-svelte library
<script>
// Gets User
const auth = getAuth();
const user = auth.currentUser;
</script>
//Navbar
<Navbar>
<NavUl>
<!-- Logged In Links -->
{#If user}
<NavLi>Team</NavLi>
<NavLi>Reps</NavLi>
<NavLi>Profile</NavLi>
<NavLi>Dark Mode Toggle</NavLi>
{else}
<!-- Logged Out Links -->
<NavLi>Dark Mode Toggle</NavLi>
{/if}
</NavUl>
</Navbar>
Related
I am new to SvelteKit and i am trying to use MSAL.js with SvelteKit, the issue is i want to implement something similar to an AuthGuard/HttpInterceptor which checks to see if the user is still logged in as they navigate around the SPA or call the external API.
I am using the OAuth 2.0 authorization code flow in Azure Active Directory B2C
within in my auth.ts file i have the following code
let accountId: string = "";
const signIn = () => {
try {
msalInstance.loginRedirect(loginRequestWithApiReadWrite);
} catch (error) {
isAuthenticated.set(false);
console.warn(error);
}
}
// This captures the response from using the redirect flow to login
await msalInstance.handleRedirectPromise()
.then(response => {
if (response) {
if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) {
handleResponse(response);
}
}
})
.catch(error => {
console.log(error);
});
async function handleResponse(response: msal.AuthenticationResult) {
if (response !== null) {
user.set(response);
isAuthenticated.set(true);
setAccount(response.account);
} else {
selectAccount();
}
}
function selectAccount() {
const currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts.length < 1) {
return;
} else if (currentAccounts.length > 1) {
const accounts = currentAccounts.filter(account =>
account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase())
&&
account.idTokenClaims?.iss?.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase())
&&
account.idTokenClaims.aud === msalConfig.auth.clientId
);
if (accounts.length > 1) {
if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) { console.log("Multiple accounts belonging to the user detected. Selecting the first one.");
setAccount(accounts[0]);
} else {
console.log("Multiple users accounts detected. Logout all to be safe.");
signOut();
};
} else if (accounts.length === 1) {
setAccount(accounts[0]);
}
} else if (currentAccounts.length === 1) {
setAccount(currentAccounts[0]);
}
}
// in case of page refresh
selectAccount();
function setAccount(account: msal.AccountInfo | null) {
if (account) {
accountId = account.homeAccountId;
}
}
const authMethods = {
signIn,
getTokenRedirect
}
In a +page.svelte file i can then import the authMethods no problem, MSAL redirects me to the microsoft sign in page, i get redirected back and can then request an access token and call external API, great all is well.
<script lang='ts'>
import authMethods from '$lib/azure/auth';
<script>
<button on:click={authMethods.signIn}>Sign In</button>
However, the issue i am having is trying to implement this so i can check to see if the user is logged in against Azure B2C using a hook.server.ts file automatically. I would like to check a variable to see if the user is authenticated and if they arnt the hooks.server will redirect them to signUp by calling the authMethod within the hook, and the user will be automatically redirected to the sign in page.
In the hooks.server.ts i have the following code:
export const handle: Handle = (async ({ event, resolve }) => {
if (isAuthenticated === false) {
authRedirect.signIn();
msalInstance.handleRedirectPromise().then((response) => {
if (response) {
console.log('login with redirect succeeded: ', response)
isAuthenticated = true;
}
}).catch((error) => {
console.log('login with redirect failed: ', error)
})
}
const response = await resolve(event);
return response;
}) satisfies Handle;
When i navigate around the SvelteKit SPA, MSAL.js keeps throwing the error below, which i know is because i am running the code from the server flow rather than in the browser, so it was my understanding that if i implement the handleRedirectPromise() in both the auth.ts file and hooks.server.ts this would await the response from the signIn event and so long as i got a response i can then set isAuthenticated to true.
errorCode: 'non_browser_environment',
errorMessage: 'Login and token requests are not supported in non-browser environments.',
subError: ''
Are you required to use the MSAL library? I have got it working with https://authjs.dev/. I was using Active Directory -https://authjs.dev/reference/oauth-providers/azure-ad but there is also a flow for B2C https://authjs.dev/reference/oauth-providers/azure-ad-b2c which I haven't tried.
Then in the hooks.server.js you can do something like the below.
import { sequence } from '#sveltejs/kit/hooks';
import { redirect } from '#sveltejs/kit';
import { SvelteKitAuth } from '#auth/sveltekit';
import AzureADProvider from '#auth/core/providers/azure-ad';
import {
AZURE_AD_CLIENT_ID,
AZURE_AD_CLIENT_SECRET,
AZURE_AD_TENANT_ID
} from '$env/static/private'
const handleAuth = SvelteKitAuth({
providers: [
AzureADProvider({
clientId: AZURE_AD_CLIENT_ID,
clientSecret: AZURE_AD_CLIENT_SECRET,
tenantId: AZURE_AD_TENANT_ID
})
]
});
async function isAuthenticatedUser({ event, resolve }) {
const session = await event.locals.getSession();
if (!session?.user && event.url.pathname !== '/') {
throw redirect(302, '/');
} else if (session?.user && event.url.pathname === '/') {
throw redirect(302, '/dashboard');
}
const response = await resolve(event);
return response;
}
export const handle = sequence(handleAuth, isAuthenticatedUser);
I'm having trouble understanding how handle functions on Netlify. In particular, I want to access the user's id when they login.
I have enabled identity on my netlify site and I can login and log out.
<button data-netlify-identity-button id="login"></button>
I have created a function identity-login that I think should handle the user's details, but I cannot see how to utilise it on the web-page
// functions/identity-login.js
exports.handler = async function (event, context) {
const { identity, user } = context.clientContext;
console.log(identity, user)
return {
statusCode: 200,
body: 'hello'
}
};
The function endpoint is
https://silly-parrot.netlify.app/.netlify/functions/identity-login
I have this in the script on my page, but I don't know how to call it or if it's correct
async function apiCall() {
const url = `/.netlify/functions/identity-login`;
try {
const response = await fetch(url);
const data = await response;
console.log(data)
return data;
} catch (err) {
console.log(err);
}
}
What should I do?
I now realise that I was taking the wrong approach. It is not necessary to use the Netlify identity-login event. The netlifyIdentity object provides the necessary functionality for identifying when the user logs in or logs out and to discover whether or not the user is logged in when the page loads (init). The user identity is contained in the user.token.access_token
The following code is within my main js script (you will of course need to access the netlifyIdentity object)
<script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
And then setup async functions to handle the authorisation events
<script>
var token = '';
var logged_in = false;
async function started() {
logged_in = false;
netlifyIdentity.on('init', async user => {
if(user) {
token = user.token.access_token;
logged_in = true;
}
console.log('init', logged_in, token);
})
netlifyIdentity.on('login', user => {
if(user) {
logged_in = true;
token = user.token.access_token;
}
console.log('log in', logged_in, token);
})
netlifyIdentity.on('logout', () => {
token = '';
logged_in = false;
console.log('log out', logged_in, token);
})
}
started()
</script>
I have simple application where user can register and login in his acc, now i want display user login on page, how i can get data from db about user that authinticated now
i also use mongodb and moongose as database
this register sistem
router.post('/register',async (req,res)=>{
const {login,mail,password} = req.body
bcrypt.hash(password, 10).then((hash) =>{
User({login:login,password:hash,mail:mail}).save()
})
res.redirect('/login')
})
this is login sistem
router.post('/',async (req,res)=>{
const {mail, password } = req.body
const user = req.body
console.log(user)
const isUserExist = await User.findOne({mail:mail})
const UserPwdCorrect = isUserExist.password
if(!isUserExist){
console.log('Логин гавно')
} else{
bcrypt.compare(password,UserPwdCorrect).then((match)=>{
if(!match){
console.log("Пароль говно")
} else{
const accessToken = createToken(user)
res.cookie("token",accessToken,{
maxAge:60*60*24*30*1000
})
res.redirect('/')
}
})
}
})
and this is what i did in jwt.js file
const {sign,verify} = require("jsonwebtoken")
const createToken = (user) =>{
const accessToken = sign({login: user.login, isAdmin:user.idAdmin,id:user.id},"jwt-secret")
return accessToken
}
const validateToken = (req,res,next) =>{
const accessToken = req.cookies["token"]
console.log(accessToken)
if(accessToken){
try {
const validtoken = verify(accessToken,"jwt-secret")
if(validtoken){
req.authenticated = true
return next()
} else{
return next()
}
} catch(err){
console.log(err)
}
}
}
Here's one solution:
Pass in userID in the JWT token. When the user clicks on the profile page, send a POST request to an endpoint that accepts the token (in a header). The backend verifies the token, extracts the userID, makes the DB call, and finally returns the response.
An advantage to this approach is that your profile route is authenticated (i.e, the route is secure).
userSchema.statics.findByCredentials = async (email, password) =>{
const user =await User.findOne({ email })
if(!user){
throw new Error("Unable to Login!")
}
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch){
throw new Error("Invalid to Login!!")
}
return user
}
const User = new mongoose.model("User",userSchema)
module.exports = User
In users i have set the routes properly too:
router.post("/users/login", async (req,res) => {
try{
const user = await User.findByCredentials(req.body.email, req.body.password)
res.send(user)
}
catch(err){
res.status(400).send()
}
})
But i get 400 Bad request error. The route is catching the error.
findByCredentials is not working?
What is my mistake??
i have stored access_token in asyncstorage,and i get that access_token.now i want to display that access token in home page.if any one know please help me.
async componentDidMount(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
console.warn(accessToken);
setTimeout(() => {
this.setState({ isLoading: false })
const { navigate } = this.props.navigation;
if(accessToken != null || accessToken == "true"){
navigate("Home");
}
else{
navigate("Login");
}
},500);
}
you can do following:
AsyncStorage.getItem(ACCESS_TOKEN).then(token=>{
if(token){
accessToken = token
// if it is an object
const key = accessToken.yourKeyName
navigate("Home", {myKey: key});
}
}).catch(err=>{
// handle error
})
then on Home page:
const {myKey} = this.props.navigation.state.params
and show myKey in <Text>on Home Page.
AsyncStorage returns a promise so you can handle it using .then.