1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.restapi.v1;
20
21 import java.io.IOException;
22 import java.time.ZoneOffset;
23 import java.time.ZonedDateTime;
24 import java.util.Date;
25 import java.util.Optional;
26
27 import org.mycore.common.MCRSession;
28 import org.mycore.common.MCRSessionMgr;
29 import org.mycore.frontend.MCRFrontendUtil;
30 import org.mycore.frontend.jersey.MCRCacheControl;
31 import org.mycore.frontend.jersey.MCRJWTUtil;
32 import org.mycore.frontend.jersey.MCRJerseyUtil;
33 import org.mycore.frontend.jersey.access.MCRRequireLogin;
34 import org.mycore.frontend.jersey.filter.access.MCRRestrictedAccess;
35 import org.mycore.restapi.v1.utils.MCRRestAPIUtil;
36
37 import com.auth0.jwt.JWT;
38 import com.auth0.jwt.exceptions.JWTVerificationException;
39
40 import jakarta.servlet.http.HttpServletRequest;
41 import jakarta.ws.rs.DefaultValue;
42 import jakarta.ws.rs.GET;
43 import jakarta.ws.rs.HeaderParam;
44 import jakarta.ws.rs.NotAuthorizedException;
45 import jakarta.ws.rs.Path;
46 import jakarta.ws.rs.Produces;
47 import jakarta.ws.rs.core.Application;
48 import jakarta.ws.rs.core.Context;
49 import jakarta.ws.rs.core.Response;
50
51
52
53
54
55
56
57
58 @Path("/auth")
59 public class MCRRestAPIAuthentication {
60
61 private static final int EXPIRATION_TIME_MINUTES = 10;
62
63 public static final String AUDIENCE = "mcr:rest-auth";
64
65 @Context
66 HttpServletRequest req;
67
68 @Context
69 Application app;
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 @GET
88 @Produces({ MCRJerseyUtil.APPLICATION_JSON_UTF8 })
89 @Path("/login")
90 @MCRCacheControl(noTransform = true,
91 noStore = true,
92 private_ = @MCRCacheControl.FieldArgument(active = true),
93 noCache = @MCRCacheControl.FieldArgument(active = true))
94 public Response authorize(@DefaultValue("") @HeaderParam("Authorization") String authorization) throws IOException {
95 if (authorization.startsWith("Basic ")) {
96
97 Optional<String> jwt = getToken(MCRSessionMgr.getCurrentSession(),
98 MCRFrontendUtil.getRemoteAddr(req));
99 if (jwt.isPresent()) {
100 return MCRJWTUtil.getJWTLoginSuccessResponse(jwt.get());
101 }
102 }
103 throw new NotAuthorizedException(
104 "Login failed. Please provide proper user name and password via HTTP Basic Authentication.",
105 MCRRestAPIUtil.getWWWAuthenticateHeader("Basic", null, app));
106 }
107
108 public static Optional<String> getToken(MCRSession session, String remoteIp) {
109 ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC);
110 return Optional.ofNullable(session)
111 .map(MCRJWTUtil::getJWTBuilder)
112 .map(b -> {
113 return b.withAudience(AUDIENCE)
114 .withClaim(MCRJWTUtil.JWT_CLAIM_IP, remoteIp)
115 .withExpiresAt(Date.from(currentTime.plusMinutes(EXPIRATION_TIME_MINUTES).toInstant()))
116 .withNotBefore(Date.from(currentTime.minusMinutes(EXPIRATION_TIME_MINUTES).toInstant()))
117 .sign(MCRJWTUtil.getJWTAlgorithm());
118 });
119 }
120
121 @GET
122 @Path("/renew")
123 @MCRRestrictedAccess(MCRRequireLogin.class)
124 @MCRCacheControl(noTransform = true,
125 noStore = true,
126 private_ = @MCRCacheControl.FieldArgument(active = true),
127 noCache = @MCRCacheControl.FieldArgument(active = true))
128 public Response renew(@DefaultValue("") @HeaderParam("Authorization") String authorization) throws IOException {
129 if (authorization.startsWith("Bearer ")) {
130
131 Optional<String> jwt = getToken(MCRSessionMgr.getCurrentSession(),
132 MCRFrontendUtil.getRemoteAddr(req));
133 if (jwt.isPresent()) {
134 return MCRJWTUtil.getJWTRenewSuccessResponse(jwt.get());
135 }
136 }
137 throw new NotAuthorizedException(
138 "Login failed. Please provide a valid JSON Web Token for authentication.",
139 MCRRestAPIUtil.getWWWAuthenticateHeader("Basic", null, app));
140 }
141
142 public static void validate(String token) throws JWTVerificationException {
143 JWT.require(MCRJWTUtil.getJWTAlgorithm())
144 .withAudience(AUDIENCE)
145 .acceptLeeway(0)
146 .build().verify(token);
147 }
148 }