def check_authorization(*args, **kwargs): if request.headers.get(ENDPOINTS_HEADER): dec = base64.b64decode(request.headers.get(ENDPOINTS_HEADER)) token = request.headers.get(AUTHORIZATION_HEADER) token = token.split(' ')[1] r = google.auth.transport.requests.Request() id_token.verify_token(token, r) else: api.abort(401, ENDPOINTS_HEADER + ' header required') return f(*args, **kwargs)
def intercept_service(self, continuation, handler_call_details): try: for k in handler_call_details.invocation_metadata: if (k.key.lower() == AUTHORIZATION_HEADER): token = k.value token = token.split(' ')[1] r = google.auth.transport.requests.Request() id_token.verify_token(token, r) return continuation(handler_call_details) return self._terminator except: return self._terminator
def check_authorization(*args, **kwargs): if request.headers.get(AUTHORIZATION_HEADER): token = request.headers.get(AUTHORIZATION_HEADER) # Do JWT validation for IAP here in a similar way to the check below for google_id_token # note, IAP uses EC so you need to validate the JWTs for that scheme # you may need to use a library which does ES256 validation using # JWK like https://github.com/mpdavis/python-jose # ...the following is for GoogleIDToken and uses RS256...i added this in just as a # middleware example token = token.split(' ')[1] r = google.auth.transport.requests.Request() id_token.verify_token(token, r) else: api.abort(401, AUTHORIZATION_HEADER + ' header required') return f(*args, **kwargs)
def _validate_id_token(token): idinfo = id_token.verify_token(token, request) if idinfo['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ]: raise ApplicationIdentityError ## TODO(dek): implement additional checks from the Google OAuth examples server-side example page return idinfo
def google_info_from_token(token): idinfo = id_token.verify_token(token, settings.GOOGLE_CLIENT_ID) if idinfo['aud'] != settings.GOOGLE_CLIENT_ID: raise GoogleAuthError('aud dont match') if idinfo['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ]: raise GoogleAuthError("Wrong issuer.") return idinfo
def gcp_instance_validation(node_fqdn, challenge_password, audience): payload = id_token.verify_token(challenge_password, request=Request(), audience=audience) if check_payload(payload): exit(0) else: exit(1)
def get_user_google(token): user = id_token.verify_token(token, google_requests.Request(), GOOGLE_CLIENT_ID) if user['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ] or 'email' not in user: raise ValueError('Wrong issuer.') else: return user
def test_verify_token(_fetch_certs, decode): result = id_token.verify_token(mock.sentinel.token, mock.sentinel.request) assert result == decode.return_value _fetch_certs.assert_called_once_with(mock.sentinel.request, id_token._GOOGLE_OAUTH2_CERTS_URL) decode.assert_called_once_with(mock.sentinel.token, certs=_fetch_certs.return_value, audience=None)
def run(): buf = [] for line in fileinput.input(): buf.append(line.rstrip('\n')) buf.append("===") audience = os.environ['AUDIENCE'] token = ''.join(buf) request = google.auth.transport.requests.Request() payload = id_token.verify_token(token, request=request, audience=audience) print(payload)
def verifyToken(token,audience): JWK_URL = 'https://www.googleapis.com/service_accounts/v1/jwk/' + cred.service_account_email X509_URL ='https://www.googleapis.com/service_accounts/v1/metadata/x509/' + cred.service_account_email request = google.auth.transport.requests.Request() id_info = id_token.verify_token( token, request, audience, certs_url=X509_URL) if id_info['iss'] != cred.service_account_email: raise ValueError('Wrong issuer.') print(id_info['sub'])
def VerifyIDToken(token, audience=None): """ Input: ID Token String, Target Audience Endpoint Output: True if token is still valid, False if invalid """ certs_url = 'https://www.googleapis.com/oauth2/v1/certs' request = google.auth.transport.requests.Request() result = id_token.verify_token(token, request, certs_url=certs_url) if audience in result['aud']: return True return False
def test_verify_token_args(_fetch_certs, decode): result = id_token.verify_token(mock.sentinel.token, mock.sentinel.request, audience=mock.sentinel.audience, certs_url=mock.sentinel.certs_url) assert result == decode.return_value _fetch_certs.assert_called_once_with(mock.sentinel.request, mock.sentinel.certs_url) decode.assert_called_once_with(mock.sentinel.token, certs=_fetch_certs.return_value, audience=mock.sentinel.audience)
def _check_token(self, bot_token: Text) -> None: # see https://developers.google.com/chat/how-tos/bots-develop#verifying_bot_authenticity # noqa: E501, W505 # and https://google-auth.readthedocs.io/en/latest/user-guide.html#identity-tokens # noqa: E501, W505 try: decoded_token = id_token.verify_token( bot_token, self.google_request, audience=self.project_id, certs_url=CERTS_URL, ) except ValueError: raise SanicException(status_code=401) if decoded_token["iss"] != "*****@*****.**": raise SanicException(status_code=401)
def post(self, request): token = {'id_token': request.data.get('id_token')} jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER try: idinfo = id_token.verify_token(token['id_token'], requests.Request()) if idinfo['aud'] not in [ FIREBASE_ANDROID_APP_ID, FIREBASE_IOS_APP_ID ]: raise ValueError('Could not verify audience.') if idinfo['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ]: raise ValueError('Wrong issuer.') if User.objects.filter( Q(email=idinfo['email']) | Q(username=idinfo['email'])).exists(): user = User.objects.get(email=idinfo['email']) else: password = User.objects.make_random_password() user = User.objects.create_user( email=idinfo['email'], username=idinfo['email'], first_name=idinfo['given_name'], last_name=idinfo['family_name'], password=password) name = idinfo['email'].replace('@', '_').replace('.', '_') + '.png' response = requester.get(idinfo['picture'], stream=True) if response.status_code != requester.codes.ok: lf = tempfile.NamedTemporaryFile() for block in response.iter_content(1024 * 8): if not block: break lf.write(block) user.image.save(name, files.File(lf)) payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) serializer = UserRegisterSerializer(user) return Response({'token': token, 'user': serializer.data}) except ValueError as err: # Invalid token content = {'message': err.__str__()} return Response(content, 500)
def check_bot_authenticity(token, audience): if not token: raise ValueError("Invalid token") if not audience: raise ValueError(f"Invalid audience") id_info = id_token.verify_token( token, requests.Request(), audience=audience, certs_url=PUBLIC_CERT_URL_PREFIX + CHAT_ISSUER, ) if id_info["iss"] != CHAT_ISSUER: raise ValueError("Wrong issuer")
def decode_token(token, audience=None) -> dict: """ Decide the given Google OpenID token into JSON representation. If an audience is given, the token is verified against the provided audience. Otherwise, verification is not attempted :param token: :param audience: :return: """ request = google.auth.transport.requests.Request() # https://github.com/googleapis/google-auth-library-python/blob/ca8d98ab2e5277e53ab8df78beb1e75cdf5321e3/google/oauth2/id_token.py#L109-L127 return id_token.verify_token(token, request, audience=audience, certs_url=CERTS_URL)
def _validate_iap_jwt(iap_jwt, expected_audience): """Validate an IAP JWT. Args: iap_jwt: The contents of the X-Goog-IAP-JWT-Assertion header. expected_audience: The Signed Header JWT audience. See https://cloud.google.com/iap/docs/signed-headers-howto for details on how to get this value. Returns: (user_id, user_email). """ decoded_jwt = id_token.verify_token( iap_jwt, requests.Request(), audience=expected_audience, certs_url='https://www.gstatic.com/iap/verify/public_key') return (decoded_jwt['sub'], decoded_jwt['email'])
def authenticate_openid(request, token): """Authenticate user using an OpenID token.""" try: unverified_claims = jwt.decode(token, None, False) except Exception as exc: raise ParseError("Bad token: {}".format(str(exc))) # Validates token following recommendations at # https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken # Does basic validation before pinging Google to do token verification exp = unverified_claims.get("exp") if exp and float(exp) < time.time(): raise ParseError("Token has expired") if unverified_claims.get("iss") != GOOGLE_ISS: raise ParseError("Invalid token issuer") if unverified_claims.get("aud") not in GOOGLE_AUDS: raise ParseError("Invalid token audience") transport = requests.Request() try: claims = id_token.verify_token(token, transport) except ValueError: raise ParseError("Token could not be verified") if not claims.get("email_verified"): raise ParseError("Email address has not been verified") email = claims.get("email") given_name = claims.get("given_name") family_name = claims.get("family_name") name = claims.get("name") if name is not None and given_name is None and family_name is None: given_name, family_name = name.split()[:1] try: user = User.objects.get(email=email) except ObjectDoesNotExist: username = generate_username(email, given_name, family_name) user = User.objects.create_user( username, email=email, first_name=given_name, last_name=family_name ) return user
def _authenticate(token): if not token: return False try: result = id_token.verify_token( id_token=token.replace("Bearer ", ""), request=Request(), certs_url=os.environ["AUTH_CERTS_URL"], ) except ValueError: return False return all(( result["aud"] == os.environ["AUTH_AUDIENCE"], result["iss"] in os.environ["AUTH_ISSUER"].split(","), result["exp"] >= time.time(), ))
def authenticate(self, request: Request) -> tuple: auth = get_authorization_header(request).decode() try: scheme, token = auth.split() except ValueError: exc = NotAuthenticatedException("Invalid Header.") exc.status_code = 401 raise exc if not scheme == "Bearer": raise NotAuthenticatedException("Invalid scheme.") try: req = google.auth.transport.requests.Request() payload = id_token.verify_token( token, request=req ) sub = payload["sub"] audience = payload["aud"] except KeyError: raise NotAuthenticatedException("Invalid validation payload.") except Exception: # noqa E722 raise NotAuthenticatedException("Invalid token.") if self.strict: from urllib.parse import urlparse request_result = urlparse( request._request.build_absolute_uri() # noqa protected ) audience_result = urlparse(audience) server_domain = request_result.netloc audience_domain = audience_result.netloc if audience_domain not in server_domain: raise NotAuthenticatedException("Invalid audience.") User = get_user_model() user = User.objects.filter(sub=sub).first() if not user or not user.is_active: raise NotAuthenticatedException("Invalid token.") return user, None
def validate_iap_jwt_and_get_email(iap_jwt, audience): """ Validate an IAP JWT and return email Source: https://cloud.google.com/iap/docs/signed-headers-howto :param iap_jwt: The contents of the X-Goog-IAP-JWT-Assertion header. :param audience: The audience to validate against """ try: decoded_jwt = id_token.verify_token( iap_jwt, requests.Request(), audience=audience, certs_url='https://www.gstatic.com/iap/verify/public_key', ) return decoded_jwt['email'] except Exception as e: logging.error(f'JWT validation error {e}') raise e
def validate_iap_jwt(iap_jwt, expected_audience): """Validate an IAP JWT. Args: iap_jwt: The contents of the X-Goog-IAP-JWT-Assertion header. expected_audience: The Signed Header JWT audience. See https://cloud.google.com/iap/docs/signed-headers-howto for details on how to get this value. Returns: (user_id, user_email, error_str). """ try: decoded_jwt = id_token.verify_token( iap_jwt, requests.Request(), audience=expected_audience, certs_url='https://www.gstatic.com/iap/verify/public_key') return (decoded_jwt['sub'], decoded_jwt['email'], '') except Exception as e: return (None, None, '**ERROR: JWT validation error {}**'.format(e))
def verify_token(token: str, audience: str) -> dict: request = google.auth.transport.requests.Request() payload = id_token.verify_token(token, request=request, audience=audience) return payload
def verify_token(token: str, audience: str) -> dict: """Verify token signature and return the token payload""" request = google.auth.transport.requests.Request() payload = id_token.verify_token(token, request=request, audience=audience) return payload
audience = 'https://yourapp.appspot.com' additional_claims = {'target_audience': 'http://yourappspot.com'} svc_creds = service_account.Credentials.from_service_account_file( '/home/srashid/gcp_misc/certs/GCPNETAppID-e65deccae47b.json') jwt_creds = jwt.Credentials.from_signing_credentials( svc_creds, audience=audience, additional_claims=additional_claims) request = google.auth.transport.requests.Request() jwt_creds.refresh(request) idt = jwt_creds.token print 'id_token: ' + idt print id_token.verify_token( idt, request, certs_url= 'https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]' ) print '--------------------------------------------------------------------------' print "++++++++++++++ 3. mint and verify google_id_token for a service_account ++++++++++++++++" # "aud": "http://yourappspot.com", # "iss": "https://accounts.google.com", from google.oauth2 import id_token from google.oauth2 import service_account import json audience = "https://www.googleapis.com/oauth2/v4/token" additional_claims = {'target_audience': 'http://yourappspot.com'}
def google_auth(): """ Method to handle user signing in via Google. This method will either connect the user to their account in the database, or create a new account for them. """ google_user_data = json.loads(request.get_data()) try: token = google_user_data['token'] idinfo = {} # Specify the CLIENT_ID of the app that accesses the backend: for client_id in CLIENT_IDS: try: idinfo = id_token.verify_token(token, requests.Request(), client_id) break except: pass # save information based on token user_email = idinfo['email'] user_first = idinfo['given_name'] user_last = idinfo['family_name'] except: return 'Invalid token', 400 user_query = { "email": google_user_data["email"] } # check that the user email is equivalent to the token if user_query['email'] != user_email: return 'Account does not match token', 400 ip_addr = request.access_route[0] # get user agent user_agent = str(request.headers.get("User-Agent")) try: os_info = user_agent.split(')')[0].split('(')[1].strip() except: os_info = "Unknown" # protects against noSQL injection if not is_clean_email(user_query['email']): #return that user made invalid email return json.dumps({"success":False, "message": "email was not clean."}), 400, {"ContentType":"application/json"} #attempt to find user with information given google_user = grades_db.users.find_one(user_query) if google_user == None: """ Options: - here we can add a user to the database with no password, and thus no one can access their account unless they sign in with their Google account - alternatively, we could redirect a user to a signup page on the frontend where they can enter a password """ #save user information user = { "first_name": google_user_data["first_name"], "last_name": "", "email": google_user_data["email"], "hashed_pw":"", "wishlist": {}, "os_info": [os_info], "ip_address_list": [ip_addr], "timestamp": get_current_time(), "location": get_location_info(ip_addr), "account_type": "Google" } try: user["last_name"] = google_user_data["last_name"] except: pass # check that user info is valid if not validate_analytics_auth(user): return json.dumps({"success":False}), 400, {"ContentType":"application/json"} #add user to db grades_db.users.insert_one(user) #send the user an email send_signup_email(google_user_data["first_name"], google_user_data["email"]) #try to find existing user current_user = grades_db.users.find_one(user) #get user info user_info = get_user_token(current_user) user_info["type"] = "SignUp" # write calls to analytics for google signup analytics = add_analytics_auth("SignUp", "Google", google_user_data["email"], google_user_data["first_name"], google_user_data["last_name"], os_info, ip_addr) # if analytics was malicious, return false if not analytics: return json.dumps({"success":False}), 400, {"ContentType":'application/json'} #return that user was signed up return json.dumps(user_info), 200, {"ContentType":"application/json"} else: """ Send user to their homepage """ user_info = get_user_token(google_user) # ensure that os and ip are clean if not is_clean_query(os_info) or not is_clean_query(ip_addr): return json.dumps({"success":False, "message": "IP/OS was not clean."}), 400, {"ContentType":"application/json"} # update os and ip address in db update_os_ip(google_user, os_info, ip_addr) # write calls to analytics for google signup analytics = add_analytics_auth("Login", "Google", google_user_data["email"], google_user_data["first_name"], google_user_data["last_name"], os_info, ip_addr) # if analytics was malicious, return false if not analytics: return json.dumps({"success":False, "message": "Analytics was not clean."}), 400, {"ContentType":'application/json'} return json.dumps(user_info), 200, {'ContentType':'application/json'} return json.dumps({'success':False}), 404, {"ContentType":"application/json"}
def authenticate_openid(request: Request, token: str) -> User: """ Authenticate user using an OpenID token. Currently only deals with tokens issued by Google. Validates token following recommendations at https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken Does basic validation before pinging Google to do token verification. If the verified email address matches an existing user then returns that user, otherwise creates a new user with the email as their primary email. """ # Run some basic checks on the token try: unverified_claims = jwt.decode(token, "", False) except Exception as exc: raise ParseError("Bad token: {}".format(str(exc))) exp = unverified_claims.get("exp") if exp and float(exp) < time.time(): raise ParseError("Token has expired") if unverified_claims.get("iss") != GOOGLE_ISS: raise ParseError("Invalid token issuer") if unverified_claims.get("aud") not in GOOGLE_AUDS: raise ParseError("Invalid token audience") # Verify the token with Google transport = requests.Request() try: claims = id_token.verify_token(token, transport) except ValueError: raise ParseError("Token could not be verified") if not claims.get("email_verified"): raise ParseError("Email address has not been verified") # Extract the verified data email = claims.get("email") given_name = claims.get("given_name", "") family_name = claims.get("family_name", "") name = claims.get("name") if name and not given_name and not family_name and len(name.split()) > 1: given_name, family_name = name.split()[:2] picture = claims.get("picture") # Check whether there is an existing user with this email address try: email_address = EmailAddress.objects.get(email__iexact=email) user = email_address.user except EmailAddress.DoesNotExist: try: user = User.objects.get(email__iexact=email) except User.DoesNotExist: user = None else: email_address = EmailAddress.objects.create( user=user, email=email, primary=True, verified=False ) if user: # Check that the user's email is verified. This prevents an attacker # from stealing a users credentials by using their email address if not email_address.verified: confirmation = EmailConfirmation.create(email_address=email_address) confirmation.send() raise AuthenticationFailed( f"For security reasons, please verify your email address first; we have sent an email to {email}." ) else: # Create a new user with a verified email address user = User.objects.create_user( username=generate_username(email, given_name, family_name), email=email, first_name=given_name, last_name=family_name, ) EmailAddress.objects.create(user=user, email=email, verified=True, primary=True) # Update the user's image if one has not already been set if picture and user.personal_account.image_is_identicon(): set_image_from_url.delay(user.personal_account.id, picture) return user
def process_key(message, context): if context: print("""This Function was triggered by messageId {} published at {}""".format(context.event_id, context.timestamp)) if CLOUD_FUNC_KEY_DATA in message: data = base64.b64decode(message[CLOUD_FUNC_KEY_DATA]).decode('utf-8') log(f"event dict data key has value: {data}", severity=LOG_SEVERITY_DEBUG) else: raise LookupError(f"event dict does not contain data key: {message}") SA_key_not_rotated = json.loads(data) key_resource = "resource" if key_resource not in SA_key_not_rotated: raise LookupError(f"Security command center finding dict does not contain key {key_resource}") resource = SA_key_not_rotated[key_resource] key_parentDisplayName = "parentDisplayName" if key_parentDisplayName not in resource: raise LookupError(f"Security command center finding dict does not contain key {key_parentDisplayName}") # extract SA from parentDisplayName project_SA_REGEX = 'projects/([\w-]+)/serviceAccounts/' + SA_regex parentDisplayName = resource[key_parentDisplayName] parentDisplayName_regex_search_result = re.search(project_SA_REGEX, parentDisplayName) if not parentDisplayName_regex_search_result or len(parentDisplayName_regex_search_result.groups()) != 2: raise ValueError( f"finding.{key_resource}.{key_parentDisplayName} failed to match regular expression {project_SA_REGEX}") GCP_SA = parentDisplayName_regex_search_result.group(2) + SA_postfix project_id = parentDisplayName_regex_search_result.group(1) log(f"extracted Google service account is {GCP_SA} and project ID is {project_id}", severity=LOG_SEVERITY_DEBUG) result = verify_config(GCP_SA, project_id, get_config_func=get_or_init_config) if result[key_abort_level] != 0: return result[key_payload] secret_project_id = result[key_payload][key_secret_project_id] # create an authenticated request to Cloud run GSA key manager for key rotation google_oauth_request = google.auth.transport.requests.Request() target_audience = result[key_payload][key_cloud_run_url] url = f"{result[key_payload][key_cloud_run_url]}/{result[key_payload][key_cloud_run_subpath]}" # local debugging vs executing in Google cloud if 'GCP_SA' in os.environ: creds = service_account.IDTokenCredentials.from_service_account_file( os.environ['GOOGLE_APPLICATION_CREDENTIALS'], target_audience=target_audience) else: creds = compute_engine.IDTokenCredentials(google_oauth_request, target_audience=target_audience, use_metadata_identity_endpoint=True) authed_session = AuthorizedSession(creds) form_data = { 'GCP_SAs': GCP_SA, # 'secret_name_prefix': 'hil_0_', key_secret_project_id: secret_project_id } sa_key_manager_response = authed_session.request(result[key_payload][key_cloud_run_verb], url=url, data=form_data) func_response = { 'content': sa_key_manager_response.content.decode('utf-8'), 'reason': sa_key_manager_response.reason, 'status_code': sa_key_manager_response.status_code, } # on success, response has json method if sa_key_manager_response.status_code == HTTPStatus.OK: func_response['content'] = sa_key_manager_response.json() # show security tokens at local debugging if 'GCP_SA' in os.environ: google_oauth_request = google.auth.transport.requests.Request() func_response['identity_token'] = creds.token func_response['Open ID Connect token verification'] = id_token.verify_token(creds.token, google_oauth_request) else: log(f"Calling {result[key_payload][key_cloud_run_url]} returns: {json.dumps(func_response)}", severity='DEBUG') return func_response
def authenticate(self, request): now = time.time() try: auth_header = request.headers['Authorization'] if auth_header.startswith('Bearer '): bearer_token = auth_header[7:] else: msg = _( f'Authorization header malformed. Expected Bearer: <token>; got {auth_header}' ) logging.info(msg) raise exceptions.AuthenticationFailed(msg) except KeyError: msg = _('Invalid Authorization header. No credentials provided.') logging.info(msg) raise exceptions.AuthenticationFailed(msg) # verify our token auth_request = google.auth.transport.requests.Request() try: token = id_token.verify_token( bearer_token, auth_request, certs_url=GoogleOpenIDAuthentication.certs_url) except ValueError as e: error_message = getattr(e, 'message', str(e)) msg = _( f'Authentication failed. Could not verify token; err = {error_message}.' ) logging.error(msg) raise exceptions.AuthenticationFailed(msg) # ensure that it has the desired claims if not all([ key in token for key in ['aud', 'iss', 'email', 'email_verified', 'iat', 'exp', 'sub'] ]): msg = _( 'Authentication failed. Token did not contain all required claims.' ) logging.info(msg) raise exceptions.AuthenticationFailed(msg) token = SimpleNamespace(**token) logging.debug(f'Access attempted with token: {token}') # the audience indicated in the token should be the visited URL __, token_audience, __ = uri_breakdown(token.aud) __, required_audience, __ = uri_breakdown(request.build_absolute_uri()) if token_audience != required_audience: msg = _( f'Authentication failed. Audience {token.aud} did not match auth endpoint {required_audience}.' ) logging.info(msg) raise exceptions.AuthenticationFailed(msg) if not token.iat < now < token.exp: msg = _(f'Authentication failed. Token outside of valid window. ' f'Issued {token.iat}, Expires: {token.exp}, Now: {now}') logging.info(msg) raise exceptions.AuthenticationFailed(msg) try: user = User.objects.get(email__iexact=token.email) except User.DoesNotExist: msg = _(f'Authentication failed. No user with email {token.email}') logging.info(msg) raise exceptions.AuthenticationFailed(msg) return user, None
def VerifyIDToken(token, certs_url, audience=None): request = google.auth.transport.requests.Request() result = id_token.verify_token(token,request,certs_url=certs_url) if audience in result['aud']: return True return False