Closed tiwarivijay123 closed 2 years ago
I just saw something similar - unsure if it's the same bug or not.
We upgraded from version 1.10.0 to 1.20.1, so I would not expect any breaking changes (assuming people follow semver). I didn't see anything impactful in the changelog.
But now when calling loginWithRedirect
(identical code other than the library version) and passing a redirect_uri
, it's redirecting to a different URL and resulting in a 404.
Using more-or-less vanilla JS on this project (Backbone). Browser Chrome.
We do try to follow semver wherever possible. If you can share your barebones project, I'd be happy to take a look. Also see if you can replicate the problem using our playground app (clone this repo and run npm install && npm start
and test out the flow using our tenant details to rule out a tenant config issue.
The local playground app appears to work with Login redirect / callback, but again I verified that this broke our app changing nothing but the version of the library. Reverting to the older version fixed the issue. I can't share our many tens of thousands of lines of proprietary code split across multiple repositories, and it would take significant time to create a minimal repro.
The simplified version is that we call:
client = new Auth0Client(options);
accessToken = await client.getTokenSilently();
user = client.user;
idToken = await client.getIdTokenClaims();
// ...
client.loginWithRedirect({
redirect_uri: '...',
appState: {
conversationUrl: '...',
sessionId: id,
},
screen_hint: 'login', // can also be login_hint depending on SSO status
});
Again, none of this changed. Also with the same Auth0 configuration this all works with the older library version.
Do you require a different redirect_uri
for different scenarios when calling loginWithRedirect
or is it basically constant?
redirect_uri
is based on modifying the current Window.location
. In theory we might be able to set it once at client creation time, but the amount of QA effort to check every possible client scenario is significant.
Can you restore the original SDK behavior?
I don't think we've changed anything specific in this area that would be causing your issue, but I've yet to deep-dive into it. Once I've had a chance to try and reproduce it this week, I'll come back to you.
I tried it's older version as well https://github.com/auth0/auth0-spa-js/releases but nothing seem to work. after successfully login it just come to back with undefined
user detail and does nothing
I'm using this version its latest but I tried older version as well but it is same
this is code which I'm trying for loginWithRedirect
@tiwarivijay123 can you check your tenant logs in your Auth0 dashboard and see if there are any errors reported in there? Do you have any errors in your browser console relating to Auth0?
Also when you reload the page and nothing happens, does it eventually do something if you wait for over a minute?
@tiwarivijay123 in your code sample, you can't simply await loginWithRedirect
and expect a result, as it will do a full-page redirect and state is lost. You need to call handleRedirectCallback
when Auth0 redirects back to your application in order to get authentication results.
Hey @stevehobbsdev I tried handleRedirectCallback
but still same
andleRedirectCallback` also but does nothing for me
I checked my Auth0 logs also no issue there
but does nothing for me
You're saying even after you call handleRedirectCalback
after you come back from Auth0 and you're still unauthenticated when you call something like getUser
or isAuthenticated
?
I can't reproduce this scenario in my environment, either yours or @TrueWill so I'm going to have to see a repro in order to progress this. If you manage to come up with a small, reproducible sample that demonstrates the issue I'd be happy to take a look.
Hi, I'm guessing the success is shown because there is a successful login but when another request is made after the redirect then we no longer have the token.. so the comment about using the "handleRedirectCallback" must be true, maybe we are not using it correctly @tiwarivijay123 ?
Here is more code to review @stevehobbsdev , any issues here please let us know..
function AuthProvider({ children }: AuthProviderProps) {
const [open, setOpen] = useState(true);
const router = useRouter();
const [state, dispatch] = useReducer(reducer, initialState);
const [isRegister, setIsRegister] = useState(false);
async function getUserInfo(user: any, isAuthenticated: any) {
try {
const response = await axios.get('user/?filter={"where":{"email":"' + user.email + '"}}');
console.log(response.data[0], 'user info');
if (response.data.length == 0) {
router.push({
pathname: PATH_AFTER_SIGNUP,
query: { returnUrl: router.asPath },
});
} else {
return response.data[0];
}
} catch (error) {
dispatch({
type: Types.init,
payload: { isAuthenticated: false, user: null },
});
}
}
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const Transition = forwardRef(function Transition(
props: TransitionProps & {
children: React.ReactElement<any, any>;
},
ref: React.Ref<unknown>
) {
return <Slide direction="up" ref={ref} {...props} />;
});
useEffect(() => {
console.log(AUTH0_API.clientId, AUTH0_API.domain, 'Domain');
const initialize = async () => {
try {
auth0 = new Auth0Client({
client_id: '',
domain: '',
redirect_uri: window.location.origin,
audience: '',
});
await auth0.checkSession();
const isAuthenticated = await auth0.isAuthenticated();
if (isAuthenticated) {
const user = await auth0.getUser();
const userInfo = await getUserInfo(user, isAuthenticated);
// dispatch({
// type: Types.init,
// payload: { isAuthenticated, user: userInfo || null },
// });
} else {
dispatch({
type: Types.init,
payload: { isAuthenticated, user: null },
});
}
} catch (err) {
console.error(err, 'erorr');
dispatch({
type: Types.init,
payload: { isAuthenticated: false, user: null },
});
}
};
initialize();
}, []);
const login = async () => {
try {
await auth0?.loginWithRedirect();
} catch (error) {
let res = error.error_description.split(':');
console.log(res[1], 'res 1');
setIsRegister(true);
return res;
}
if (isRegister == false) {
const isAuthenticated = await auth0?.isAuthenticated();
const user = await auth0.getUser();
console.log(user, isAuthenticated, 'auth response');
if (isAuthenticated) {
const user = await auth0?.getUser();
const accessToken = await auth0.getTokenSilently();
axios.defaults.baseURL = '';
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
axios.interceptors.request.use(
(request) => {
console.log(request);
// Edit request config
return request;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log(response);
// Edit response config
return response;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
const userInfo = await getUserInfo(user, isAuthenticated);
localStorage.setItem('userId', userInfo.id);
localStorage.setItem('accessToken', accessToken);
dispatch({ type: Types.login, payload: { user: userInfo || null } });
} else {
}
} else {
const isAuthenticated = await auth0?.isAuthenticated();
const user = await auth0.getUser();
console.log(user, isAuthenticated, 'auth response');
if (isAuthenticated) {
const user = await auth0?.getUser();
const accessToken = await auth0.getTokenSilently();
localStorage.setItem('signupEmail', user?.email);
window.localStorage.setItem('accessToken', accessToken);
axios.defaults.baseURL = '';
axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
axios.interceptors.request.use(
(request) => {
console.log(request);
// Edit request config
return request;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
axios.interceptors.response.use(
(response) => {
console.log(response);
// Edit response config
return response;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);
}
// setIsRegister(false);
router.push({
pathname: PATH_AFTER_SIGNUP,
query: { returnUrl: router.asPath },
});
// dispatch({ type: Types.Register, payload: { user: user } });
}
};
const logout = () => {
auth0?.logout();
dispatch({ type: Types.logout });
};
console.log(state, 'state');
return (
<AuthContext.Provider
value={{
...state,
method: 'auth0',
user: {
additionalInfo: state?.user,
id: state?.user?.sub,
photoURL: state?.user?.avatar_signed_url,
email: state?.user?.email,
displayName: state?.user?.contact?.fullname,
role: state?.user?.contact?.header,
},
login,
logout,
}}
>
{children}
</AuthContext.Provider>
);
}
Any success ?
Our company is planning to write up a minimal repro (probably not in Next.js though), but it may take some time. If anyone gets to it before then please share! Thanks!
Also for Next.js folks, I wonder if https://github.com/auth0/auth0-react would be a better option.
@tiwarivijay123 I've got some feedback on your code sample I can share tomorrow.
@TrueWill we also have an SDK built specially for Next.js in case that's an even better fit: https://github.com/auth0/nextjs-auth0
Our company is planning to write up a minimal repro (probably not in Next.js though), but it may take some time. If anyone gets to it before then please share! Thanks!
Also for Next.js folks, I wonder if https://github.com/auth0/auth0-react would be a better option.
I used this but for some reason in case of loginWithRedirect it is same @TrueWill
@tiwarivijay123 @Mr-Fraser So the thing to keep in mind is that there are two parts to the flow when using loginWithRedirect
:
loginWithRedirect
to start the flow and send the user to the login page (this is a top-level browser redirect)handleRedirectCallback
in order to grab the code
value and exchange it for tokens, effectively creating a session within the SDK.So this type of flow from your code sample will not work properly:
try {
await auth0?.loginWithRedirect();
} catch (error) {
let res = error.error_description.split(':');
console.log(res[1], 'res 1');
setIsRegister(true);
return res;
}
// You'll never be authenticated without first calling `handleRedirectCallback`
const isAuthenticated = await auth0?.isAuthenticated();
const user = await auth0.getUser();
console.log(user, isAuthenticated, 'auth response');
What you should do is exit out of that flow immediately after calling loginWithRedirect
, since a top-level redirect will be performed anyway. Note that when you're using loginWithPopup
, what you have will work (and that's consistent with your issue report above) in that you can await loginWithPopup()
and get the tokens back, since there's no top-level redirect.
The trick is detecting when you should call handleRedirectCallback
when Auth0 redirects back to your app. In our React/Angular/Vue SDKs, we handle this internally essentially by checking for the presence of the code
or state
values on page load. Something like this modified example from Auth0 Vue:
if (
(window.location.search.includes('code=') || window.location.search.includes('error=')) &&
window.location.search.includes('state=')
) {
const result = await auth0.handleRedirectCallback();
// Remove the params from the URL
window.history.replaceState({}, '', '/');
// This should now work
const user = await auth0.getUser();
const isAuthenticated = await auth0.isAuthenticated();
return result;
} else {
await auth0.checkSession();
}
Note that this is where we also call checkSession
if we're not coming from an Auth0 redirect (it's a normal page refresh or something like that).
Hopefully that gives you some food for thought as to how to manage this in your own application. What I would also suggest is running through one of our SPA quickstarts as an example to get a feel for how the flow should work in a cut-down environment. In particular the one for vanilla JavaScript as it lets you see the nuts and bolts of it a bit more (the React/Angular/Vue hide some of this for your convenience).
Hope that helps, and do let me know how you get on.
@tiwarivijay123 I've got some feedback on your code sample I can share tomorrow.
@TrueWill we also have an SDK built specially for Next.js in case that's an even better fit: https://github.com/auth0/nextjs-auth0
@stevehobbsdev can you let me know how I can get access token in this SDK https://github.com/auth0/nextjs-auth0 ?
// pages/api/products.js
import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0';
export default withApiAuthRequired(async function products(req, res) {
// If your Access Token is expired and you have a Refresh Token
// `getAccessToken` will fetch you a new one using the `refresh_token` grant
const { accessToken } = await getAccessToken(req, res, {
scopes: ['read:products']
});
const response = await fetch('https://api.example.com/products', {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
const products = await response.json();
res.status(200).json(products);
});
I'm trying this code
any update for me ?
@tiwarivijay123 Thanks for your patience. While we do try to respond in a timely fashion, you're unlikely to get a deep response over a weekend.
The code you have shown here is the example from Next.js Auth0 and so is our recommended way to get the access token. Does this not work for you? If you have any issues using that SDK, please raise it on the Next.js Auth0 repository.
If you're now using the Next.js Auth0 SDK, can this issue now be closed or do require further help?
Yeah ok thanks @stevehobbsdev I guess now you can close this issue
Problem
What was the expected behavior?
Reproduction
npm install @auth0/auth0-spa-js
Environment
next js
( used this SDK also https://auth0.com/docs/quickstart/webapp/nextjs/01-login ) same behaviour for redirect login now using https://github.com/auth0/auth0-spa-js. its behaviour is also same