test login
This commit is contained in:
parent
ca7790bfdf
commit
9066a51025
@ -1,16 +1,90 @@
|
|||||||
package fr.titionfire;
|
package fr.titionfire;
|
||||||
|
|
||||||
|
import io.quarkus.oidc.IdToken;
|
||||||
|
import io.quarkus.oidc.RefreshToken;
|
||||||
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import org.eclipse.microprofile.jwt.JsonWebToken;
|
||||||
|
import org.jboss.resteasy.reactive.NoCache;
|
||||||
|
|
||||||
@Path("/hello")
|
@Path("/hello")
|
||||||
public class ExampleResource {
|
public class ExampleResource {
|
||||||
|
|
||||||
|
/*@Inject
|
||||||
|
@IdToken
|
||||||
|
JsonWebToken idToken;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
public String hello() {
|
public String hello() {
|
||||||
return "Hello from RESTEasy Reactive";
|
return "Hello, " + idToken.getClaim("name");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point for the ID Token issued by the OpenID Connect Provider
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
@IdToken
|
||||||
|
JsonWebToken idToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point for the Access Token issued by the OpenID Connect Provider
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
JsonWebToken accessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point for the Refresh Token issued by the OpenID Connect Provider
|
||||||
|
*/
|
||||||
|
@Inject
|
||||||
|
RefreshToken refreshToken;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityIdentity securityIdentity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tokens available to the application. This endpoint exists only for demonstration purposes, you should not
|
||||||
|
* expose these tokens in a real application.
|
||||||
|
*
|
||||||
|
* @return a HTML page containing the tokens available to the application
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces("text/html")
|
||||||
|
@NoCache
|
||||||
|
public String getTokens() {
|
||||||
|
StringBuilder response = new StringBuilder().append("<html>")
|
||||||
|
.append("<body>")
|
||||||
|
.append("<ul>");
|
||||||
|
|
||||||
|
|
||||||
|
Object userName = this.idToken.getClaim("preferred_username");
|
||||||
|
|
||||||
|
if (userName != null) {
|
||||||
|
response.append("<li>username: ").append(userName.toString()).append("</li>");
|
||||||
|
}
|
||||||
|
if (userName != null) {
|
||||||
|
response.append("<li>username: ").append(this.idToken.toString()).append("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object scopes = this.accessToken.getClaim("scope");
|
||||||
|
|
||||||
|
if (scopes != null) {
|
||||||
|
response.append("<li>scopes: ").append(scopes.toString()).append("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopes != null) {
|
||||||
|
response.append("<li>scopes: ").append(this.accessToken.toString()).append("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopes != null) {
|
||||||
|
response.append("<li>getRoles: ").append(this.securityIdentity.getRoles()).append("</li>");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.append("<li>refresh_token: ").append(refreshToken.getToken() != null).append("</li>");
|
||||||
|
|
||||||
|
return response.append("</ul>").append("</body>").append("</html>").toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java
Normal file
45
src/main/java/fr/titionfire/ffsaf/rest/AuthEndpoints.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package fr.titionfire.ffsaf.rest;
|
||||||
|
|
||||||
|
import io.quarkus.security.Authenticated;
|
||||||
|
import io.quarkus.security.identity.SecurityIdentity;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
@Path("/auth")
|
||||||
|
public class AuthEndpoints {
|
||||||
|
|
||||||
|
@ConfigProperty(name = "login_redirect")
|
||||||
|
String redirect;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SecurityIdentity securityIdentity;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public Boolean auth() {
|
||||||
|
return !securityIdentity.isAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/userinfo")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public String userinfo() {
|
||||||
|
return securityIdentity.getPrincipal().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/login")
|
||||||
|
@Authenticated
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public Response login() throws URISyntaxException {
|
||||||
|
return Response.temporaryRedirect(new URI(redirect)).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -87,6 +87,7 @@ public class CombEndpoints {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{id}/photo")
|
@Path("{id}/photo")
|
||||||
|
@RolesAllowed("federation_admin")
|
||||||
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
|
||||||
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
|
Future<Pair<File, byte[]>> future = CompletableFuture.supplyAsync(() -> {
|
||||||
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id));
|
||||||
|
|||||||
@ -25,6 +25,8 @@ quarkus.oidc.tls.verification=required
|
|||||||
|
|
||||||
quarkus.http.limits.max-body-size=10M
|
quarkus.http.limits.max-body-size=10M
|
||||||
|
|
||||||
|
quarkus.oidc.token-state-manager.split-tokens=true
|
||||||
|
|
||||||
database.prefix = test2_
|
database.prefix = test2_
|
||||||
database.database=ffsaf
|
database.database=ffsaf
|
||||||
database.hostname=localhost
|
database.hostname=localhost
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import LogoIcon from '../assets/FFSSAF-bord-blanc-fond-transparent.webp'
|
|||||||
import './Nav.css'
|
import './Nav.css'
|
||||||
import {NavLink} from "react-router-dom";
|
import {NavLink} from "react-router-dom";
|
||||||
import {useAuth, useAuthDispatch} from "../hooks/useAuth.jsx";
|
import {useAuth, useAuthDispatch} from "../hooks/useAuth.jsx";
|
||||||
import {logout, redirect_auth_page} from "../utils/auth.js";
|
import {login, logout} from "../utils/auth.js";
|
||||||
|
|
||||||
|
|
||||||
export function Nav() {
|
export function Nav() {
|
||||||
@ -55,7 +55,7 @@ function LoginMenu() {
|
|||||||
|
|
||||||
return <li className="nav-item">
|
return <li className="nav-item">
|
||||||
{!is_authenticated ? (
|
{!is_authenticated ? (
|
||||||
<div className="nav-link" onClick={() => redirect_auth_page()}>Connexion</div>
|
<div className="nav-link" onClick={() => login()}>Connexion</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="nav-link" onClick={() => {
|
<div className="nav-link" onClick={() => {
|
||||||
logout(token, refresh).then(() => dispatch({type: 'invalidate'}))
|
logout(token, refresh).then(() => dispatch({type: 'invalidate'}))
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {sendTokenRequestPart2} from "../utils/auth.js";
|
import {login_redirect} from "../utils/auth.js";
|
||||||
import {useEffect, useRef} from "react";
|
import {useEffect, useRef} from "react";
|
||||||
|
|
||||||
export const AuthCallback = () => {
|
export const AuthCallback = () => {
|
||||||
@ -8,8 +8,7 @@ export const AuthCallback = () => {
|
|||||||
if (isInit.current)
|
if (isInit.current)
|
||||||
return;
|
return;
|
||||||
isInit.current = true
|
isInit.current = true
|
||||||
const params = new URLSearchParams(window.location.search);
|
login_redirect();
|
||||||
sendTokenRequestPart2(params.get('code'), params.get('state'));
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import {createContext, useContext, useEffect, useReducer} from "react";
|
import {createContext, useContext, useEffect, useReducer} from "react";
|
||||||
import {sendTokenRequest} from "../utils/auth.js";
|
|
||||||
|
|
||||||
const AuthContext = createContext(undefined);
|
const AuthContext = createContext(undefined);
|
||||||
const AuthDispatchContext = createContext(null);
|
const AuthDispatchContext = createContext(null);
|
||||||
@ -15,23 +14,6 @@ export function useAuthDispatch() {
|
|||||||
export function KeycloakContextProvider({children}) {
|
export function KeycloakContextProvider({children}) {
|
||||||
const [auth, dispatch] = useReducer(authReducer, initialAuth);
|
const [auth, dispatch] = useReducer(authReducer, initialAuth);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (auth.token === undefined || !auth.is_authenticated)
|
|
||||||
return;
|
|
||||||
const jwt = JSON.parse(atob(auth.token.split('.')[1]));
|
|
||||||
|
|
||||||
const ref = setTimeout(() => {
|
|
||||||
if (auth.refresh !== undefined && auth.refresh !== null)
|
|
||||||
sendTokenRequest(auth.refresh).then(data => dispatch({
|
|
||||||
type: 'update',
|
|
||||||
token: data.access_token,
|
|
||||||
refresh: data.refresh_token,
|
|
||||||
})
|
|
||||||
).catch(() => dispatch({type: 'invalidate'}));
|
|
||||||
}, jwt.exp * 1000 - new Date().getTime() - 1000)
|
|
||||||
return () => clearTimeout(ref);
|
|
||||||
}, [auth])
|
|
||||||
|
|
||||||
return <AuthContext.Provider value={auth}>
|
return <AuthContext.Provider value={auth}>
|
||||||
<AuthDispatchContext.Provider value={dispatch}>
|
<AuthDispatchContext.Provider value={dispatch}>
|
||||||
{children}
|
{children}
|
||||||
@ -61,8 +43,6 @@ function authReducer(auth, action) {
|
|||||||
case 'invalidate': {
|
case 'invalidate': {
|
||||||
return {
|
return {
|
||||||
...auth,
|
...auth,
|
||||||
token: undefined,
|
|
||||||
refresh: undefined,
|
|
||||||
is_authenticated: false
|
is_authenticated: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,8 +53,6 @@ function authReducer(auth, action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialAuth = {
|
const initialAuth = {
|
||||||
token: undefined,
|
|
||||||
refresh: undefined,
|
|
||||||
is_authenticated: undefined,
|
is_authenticated: undefined,
|
||||||
data: undefined,
|
data: undefined,
|
||||||
}
|
}
|
||||||
@ -4,93 +4,30 @@ const auth_url = import.meta.env.VITE_AUTH_URL;
|
|||||||
const vite_url = import.meta.env.VITE_URL;
|
const vite_url = import.meta.env.VITE_URL;
|
||||||
const client_id = import.meta.env.VITE_CLIENT_ID;
|
const client_id = import.meta.env.VITE_CLIENT_ID;
|
||||||
|
|
||||||
|
const api_url = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
export function check_validity(online_callback = () => {
|
export function check_validity(online_callback = () => {
|
||||||
}) {
|
}) {
|
||||||
const token = localStorage.getItem("access_token")
|
return axios.get(`${api_url}/auth`).then(data => {
|
||||||
if (token !== undefined && token !== null) {
|
console.log(data.data)
|
||||||
const jwt = JSON.parse(atob(token.split('.')[1]));
|
online_callback(data.data);
|
||||||
|
}).catch(() => {
|
||||||
if (jwt.exp < new Date().getTime() / 1000 + 1)
|
online_callback(false);
|
||||||
if (online_callback) online_callback(false); else return false;
|
|
||||||
|
|
||||||
if (online_callback) {
|
|
||||||
return axios.get(`${auth_url}/userinfo`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
online_callback(true);
|
|
||||||
}).catch(() => {
|
|
||||||
online_callback(false);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (online_callback) online_callback(true); else return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (online_callback) online_callback(false); else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendTokenRequest(refresh) {
|
|
||||||
return axios.post(`${auth_url}/token`, {
|
|
||||||
client_id: client_id,
|
|
||||||
redirect_uri: `${vite_url}/complete/auth/`,
|
|
||||||
grant_type: 'refresh_token',
|
|
||||||
refresh_token: refresh
|
|
||||||
}, {
|
|
||||||
headers: {
|
|
||||||
"Accept": "*/*",
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
}
|
|
||||||
}).then(data => {
|
|
||||||
localStorage.setItem("access_token", data.data.access_token);
|
|
||||||
localStorage.setItem("refresh_token", data.data.refresh_token);
|
|
||||||
|
|
||||||
return data.data;
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err)
|
|
||||||
alert("Failed get token from refresh");
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendTokenRequestPart2(code, state) {
|
export function login() {
|
||||||
axios.post(`${auth_url}/token`, {
|
sessionStorage.setItem("next", window.location.href)
|
||||||
client_id: client_id,
|
window.location.href = `${api_url}/auth/login`;
|
||||||
redirect_uri: `${vite_url}/complete/auth/`,
|
|
||||||
code: code,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
}, {
|
|
||||||
headers: {
|
|
||||||
"Accept": "application/x-www-form-urlencoded",
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
}
|
|
||||||
}).then(data => {
|
|
||||||
localStorage.setItem("access_token", data.data.access_token);
|
|
||||||
localStorage.setItem("refresh_token", data.data.refresh_token);
|
|
||||||
|
|
||||||
const next = localStorage.getItem("cb_" + state)
|
|
||||||
if (next) {
|
|
||||||
clearNext()
|
|
||||||
window.location.href = next
|
|
||||||
} else {
|
|
||||||
window.location.href = import.meta.env.VITE_URL
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.log(err)
|
|
||||||
// redirect_auth_page()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearNext() {
|
export function login_redirect() {
|
||||||
const arr = []; // Array to hold the keys
|
const next = sessionStorage.getItem("next")
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
if (next) {
|
||||||
if (localStorage.key(i).substring(0, 3) === 'cb_') {
|
sessionStorage.removeItem("next")
|
||||||
arr.push(localStorage.key(i));
|
window.location.href = next
|
||||||
}
|
} else {
|
||||||
}
|
window.location.href = import.meta.env.VITE_URL
|
||||||
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
localStorage.removeItem(arr[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user