@@ -79,6 +79,51 @@ class AuthenticateRequestOptions:
7979 clock_skew_in_ms : int = 5000
8080
8181def authenticate_request (request : Requestish , options : AuthenticateRequestOptions ) -> RequestState :
82+
83+ def __compute_org_permissions (claims : Dict [str , Any ]) -> List [str ]:
84+ features_str = claims .get ("fea" )
85+ if features_str is None :
86+ return []
87+
88+ org_claims = claims .get ("o" , {})
89+ permissions_str = org_claims .get ("per" )
90+ mappings_str = org_claims .get ("fpm" )
91+
92+ if not all (isinstance (s , str ) for s in [permissions_str , mappings_str ]):
93+ return []
94+
95+ features = features_str .split ("," )
96+ permissions = permissions_str .split ("," )
97+ mappings = mappings_str .split ("," )
98+
99+ org_permissions = []
100+
101+ for idx in range (len (mappings )):
102+ if idx >= len (features ):
103+ continue
104+
105+ mapping = mappings [idx ]
106+ feature_parts = features [idx ].split (":" )
107+ if len (feature_parts ) != 2 :
108+ continue
109+
110+ scope , feature = feature_parts
111+ if "o" not in scope :
112+ continue
113+
114+ try :
115+ binary = bin (int (mapping ))[2 :].lstrip ("0" )
116+ except ValueError :
117+ continue
118+
119+ reversed_binary = binary [::- 1 ]
120+
121+ for i , bit in enumerate (reversed_binary ):
122+ if bit == "1" and i < len (permissions ):
123+ org_permissions .append (f"org:{ feature } :{ permissions [i ]} " )
124+
125+ return org_permissions
126+
82127 """ Authenticates the session token. Networkless if the options.jwt_key is provided.
83128 Otherwise, performs a network call to retrieve the JWKS from Clerk's Backend API.
84129 """
@@ -104,25 +149,53 @@ def get_session_token(request: Requestish) -> Optional[str]:
104149
105150
106151 session_token = get_session_token (request )
152+
107153 if session_token is None :
108154 return RequestState (status = AuthStatus .SIGNED_OUT , reason = AuthErrorReason .SESSION_TOKEN_MISSING )
109155
110- if options .secret_key is None :
111- return RequestState (status = AuthStatus .SIGNED_OUT , reason = AuthErrorReason .SECRET_KEY_MISSING )
112-
113156 try :
114- payload = verify_token (
115- session_token ,
116- VerifyTokenOptions (
117- audience = options .audience ,
118- authorized_parties = options .authorized_parties ,
119- secret_key = options .secret_key ,
120- clock_skew_in_ms = options .clock_skew_in_ms ,
121- jwt_key = options .jwt_key ,
122- ),
123- )
157+ if options .secret_key :
158+ payload = verify_token (
159+ session_token ,
160+ VerifyTokenOptions (
161+ audience = options .audience ,
162+ authorized_parties = options .authorized_parties ,
163+ secret_key = options .secret_key ,
164+ clock_skew_in_ms = options .clock_skew_in_ms ,
165+ jwt_key = None ,
166+ ),
167+ )
168+ elif options .jwt_key :
169+ payload = verify_token (
170+ session_token ,
171+ VerifyTokenOptions (
172+ audience = options .audience ,
173+ authorized_parties = options .authorized_parties ,
174+ secret_key = None ,
175+ clock_skew_in_ms = options .clock_skew_in_ms ,
176+ jwt_key = options .jwt_key ,
177+ ),
178+ )
179+ else :
180+ return RequestState (status = AuthStatus .SIGNED_OUT , reason = AuthErrorReason .SECRET_KEY_MISSING )
181+
182+ if payload is not None and payload .get ("v" ) == 2 :
183+ org_claims = payload .get ("o" , {})
184+ if org_claims :
185+ payload ["org_id" ] = org_claims .get ("id" )
186+ payload ["org_slug" ] = org_claims .get ("slg" )
187+ payload ["org_role" ] = org_claims .get ("rol" )
188+
189+ org_permissions = __compute_org_permissions (payload )
190+ if org_permissions :
191+ payload ["org_permissions" ] = org_permissions
124192
125193 return RequestState (status = AuthStatus .SIGNED_IN , token = session_token , payload = payload )
126194
127195 except TokenVerificationError as e :
128196 return RequestState (status = AuthStatus .SIGNED_OUT , reason = e .reason )
197+
198+
199+
200+
201+
0 commit comments