1818use OCP \AppFramework \Http \Attribute \NoCSRFRequired ;
1919use OCP \AppFramework \Http \Attribute \PublicPage ;
2020use OCP \AppFramework \Http \DataResponse ;
21+ use OCP \AppFramework \Http \JSONResponse ;
2122use OCP \AppFramework \Utility \ITimeFactory ;
2223use OCP \Authentication \Exceptions \ExpiredTokenException ;
2324use OCP \Authentication \Exceptions \InvalidTokenException ;
2425use OCP \Authentication \Token \IToken ;
26+ use OCP \Federation \ICloudIdManager ;
2527use OCP \IAppConfig ;
2628use OCP \IRequest ;
2729use OCP \Security \ISecureRandom ;
30+ use OCP \Security \Signature \Exceptions \IdentityNotFoundException ;
2831use OCP \Security \Signature \Exceptions \IncomingRequestException ;
2932use OCP \Security \Signature \Exceptions \SignatoryNotFoundException ;
3033use OCP \Security \Signature \Exceptions \SignatureException ;
3134use OCP \Security \Signature \Exceptions \SignatureNotFoundException ;
3235use OCP \Security \Signature \IIncomingSignedRequest ;
3336use OCP \Security \Signature \ISignatureManager ;
3437use OCP \Security \Signature \Model \Signatory ;
38+ use OCP \Share \Exceptions \ShareNotFound ;
3539use OCP \Share \IManager as IShareManager ;
3640use Psr \Log \LoggerInterface ;
3741
@@ -51,19 +55,45 @@ public function __construct(
5155 private readonly IAppConfig $ appConfig ,
5256 private readonly OcmTokenMapMapper $ ocmTokenMapMapper ,
5357 private readonly IShareManager $ shareManager ,
58+ private readonly ICloudIdManager $ cloudIdManager ,
5459 ) {
5560 parent ::__construct ('cloud_federation_api ' , $ request );
5661 }
5762
63+ /**
64+ * Resolve the signer origin from the refresh token's share, or null.
65+ *
66+ * @param string $code refresh token
67+ * @return string|null signer origin, or null if it cannot be determined
68+ */
69+ private function resolveOriginFromRefreshToken (string $ code ): ?string {
70+ if ($ code === '' ) {
71+ return null ;
72+ }
73+ try {
74+ $ share = $ this ->shareManager ->getShareByToken ($ code );
75+ $ sharedWith = $ share ->getSharedWith ();
76+ if ($ sharedWith === null || $ sharedWith === '' ) {
77+ return null ;
78+ }
79+ $ remote = $ this ->cloudIdManager ->resolveCloudId ($ sharedWith )->getRemote ();
80+ return $ this ->signatureManager ->extractIdentityFromUri ($ remote );
81+ } catch (ShareNotFound |IdentityNotFoundException |\InvalidArgumentException ) {
82+ return null ;
83+ }
84+ }
85+
5886 /**
5987 * Verify the signature of incoming request if available
6088 *
89+ * @param string|null $origin sender origin, or null if unknown
90+ *
6191 * @return IIncomingSignedRequest|null null if remote does not support signed requests
6292 * @throws IncomingRequestException if signature is required but invalid
6393 */
64- private function verifySignedRequest (): ?IIncomingSignedRequest {
94+ private function verifySignedRequest (? string $ origin ): ?IIncomingSignedRequest {
6595 try {
66- $ signedRequest = $ this ->signatureManager ->getIncomingSignedRequest ($ this ->signatoryManager );
96+ $ signedRequest = $ this ->signatureManager ->getIncomingSignedRequest ($ this ->signatoryManager , null , $ origin );
6797 $ this ->logger ->debug ('Token request signature verified ' , [
6898 'origin ' => $ signedRequest ->getOrigin ()
6999 ]);
@@ -109,6 +139,25 @@ private function resolveJwtSigningKey(string $privateKeyPem): array {
109139 throw new \RuntimeException ('Unsupported signatory key type for JWT access token ' );
110140 }
111141
142+ /**
143+ * Serve the local JWK Set
144+ *
145+ * @return JSONResponse<Http::STATUS_OK, array{keys: list<array<string, string>>}, array{}>
146+ *
147+ * 200: JWK Set returned
148+ */
149+ #[PublicPage]
150+ #[NoCSRFRequired]
151+ public function jwks (): JSONResponse {
152+ $ keys = [];
153+ try {
154+ $ keys = $ this ->signatoryManager ->getLocalJwks ();
155+ } catch (\Throwable $ e ) {
156+ $ this ->logger ->warning ('failed to build local JWKs ' , ['exception ' => $ e ]);
157+ }
158+ return new JSONResponse (['keys ' => $ keys ]);
159+ }
160+
112161 /**
113162 * Exchange a refresh token for a short-lived access token
114163 *
@@ -126,7 +175,7 @@ private function resolveJwtSigningKey(string $privateKeyPem): array {
126175 #[FrontpageRoute(verb: 'POST ' , url: '/api/v1/access-token ' )]
127176 public function accessToken (string $ grant_type = '' , string $ code = '' ): DataResponse {
128177 try {
129- $ signedRequest = $ this ->verifySignedRequest ();
178+ $ signedRequest = $ this ->verifySignedRequest ($ this -> resolveOriginFromRefreshToken ( $ code ) );
130179 } catch (IncomingRequestException $ e ) {
131180 $ this ->logger ->warning ('Token request signature verification failed ' , [
132181 'exception ' => $ e
0 commit comments