Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Updated Auth API #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ There is a pylint configuration file ([`.pylintrc`](../.pylintrc)) at the root o
repository. This enables you to invoke pylint directly from the command line:

```
pylint firebase
pylint firebase_admin
```

However, it is recommended that you use the [`lint.sh`](../lint.sh) bash script to invoke
pylint. This script will run the linter on both `firebase` and the corresponding
pylint. This script will run the linter on both `firebase_admin` and the corresponding
`tests` module. It suprresses some of the noisy warnings that get generated
when running pylint on test code. Note that by default `lint.sh` will only
validate the locally modified source files. To validate all source files,
Expand All @@ -117,7 +117,7 @@ by pylint, and only output the detected issues. If you wish to obtain the
comprehensive reports, run pylint from command-line with the `-r` flag.

```
pylint -r yes firebase
pylint -r yes firebase_admin
```

### Unit Testing
Expand Down Expand Up @@ -153,7 +153,7 @@ file in the Git repository, and execute test cases in each of those environments

Here are some highlights of the directory structure and notable source files

* `firebase/` - Source directory for the `firebase` module.
* `firebase_admin/` - Source directory for the `firebase_admin` module.
* `tests/` - Unit tests.
* `data/` - Provides mocks for several variables as well as mock service account keys.
* `.github/` - Contribution instructions as well as issue and pull request templates.
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.cache/
.tox/
*.egg-info/
*~
37 changes: 23 additions & 14 deletions firebase/__init__.py → firebase_admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Firebase Admin SDK for Python."""
import threading

from firebase_admin import credentials


_apps = {}
_apps_lock = threading.RLock()

_DEFAULT_APP_NAME = '[DEFAULT]'


def initialize_app(options, name=_DEFAULT_APP_NAME):
def initialize_app(credential=None, options=None, name=_DEFAULT_APP_NAME):
"""Initializes and returns a new App instance.

Creates a new App intance using the specified options
Expand All @@ -18,7 +20,9 @@ def initialize_app(options, name=_DEFAULT_APP_NAME):
App constructor.

Args:
options: A dictionary of configuration options.
credential: A credential object used to initialize the SDK (optional). If none is provided,
Google Application Default Credentials are used.
options: A dictionary of configuration options (optional).
name: Name of the app (optional).

Returns:
Expand All @@ -28,7 +32,7 @@ def initialize_app(options, name=_DEFAULT_APP_NAME):
ValueError: If the app name is already in use, or any of the
provided arguments are invalid.
"""
app = App(name, options)
app = App(name, credential, options)
with _apps_lock:
if app.name not in _apps:
_apps[app.name] = app
Expand Down Expand Up @@ -112,17 +116,12 @@ class _AppOptions(object):
"""A collection of configuration options for an App."""

def __init__(self, options):
if options is None:
options = {}
if not isinstance(options, dict):
raise ValueError('Illegal Firebase app options type: {0}. Options '
'must be a dictionary.'.format(type(options)))
self._credential = options.get('credential', None)
if not self._credential:
raise ValueError('Options must be a dict containing at least a'
' "credential" key.')

@property
def credential(self):
return self._credential
self._options = options


class App(object):
Expand All @@ -132,26 +131,36 @@ class App(object):
common to all Firebase APIs.
"""

def __init__(self, name, options):
def __init__(self, name, credential, options):
"""Constructs a new App using the provided name and options.

Args:
name: Name of the application.
credential: A credential object.
options: A dictionary of configuration options.

Raises:
ValueError: If an argument is None or invalid.
"""
if not name or not isinstance(name, basestring):
raise ValueError('Illegal Firebase app name "{0}" provided. App '
'name must be a non-empty string.'.format(name))
raise ValueError('Illegal Firebase app name "{0}" provided. App name must be a '
'non-empty string.'.format(name))
self._name = name

if not isinstance(credential, credentials.Base):
raise ValueError('Illegal Firebase credential provided. App must be initialized '
'with a valid credential instance.')
self._credential = credential
self._options = _AppOptions(options)

@property
def name(self):
return self._name

@property
def credential(self):
return self._credential

@property
def options(self):
return self._options
114 changes: 18 additions & 96 deletions firebase/auth.py → firebase_admin/auth.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
"""Firebase Authentication Library.
"""Firebase Authentication module.

This library contains helper methods and utilities for minting and verifying
This module contains helper methods and utilities for minting and verifying
JWTs used for authenticating against Firebase services.
"""

import json
import os
import sys
import threading
import time

import httplib2
from oauth2client import client
from oauth2client import crypt

import firebase
from firebase import jwt
import firebase_admin
from firebase_admin import credentials
from firebase_admin import jwt

_auth_lock = threading.Lock()

Expand All @@ -30,16 +27,16 @@

def _get_initialized_app(app):
if app is None:
return firebase.get_app()
elif isinstance(app, firebase.App):
initialized_app = firebase.get_app(app.name)
return firebase_admin.get_app()
elif isinstance(app, firebase_admin.App):
initialized_app = firebase_admin.get_app(app.name)
if app is not initialized_app:
raise ValueError('Illegal app argument. App instance not '
'initialized via the firebase module.')
return app
else:
raise ValueError('Illegal app argument. Argument must be of type '
' firebase.App, but given "{0}".'.format(type(app)))
' firebase_admin.App, but given "{0}".'.format(type(app)))


def _get_token_generator(app):
Expand Down Expand Up @@ -99,7 +96,7 @@ def verify_id_token(id_token, app=None):

Raises:
ValueError: If the input parameters are invalid, or if the App was not
initialized with a CertificateCredential.
initialized with a credentials.Certificate.
AppIdenityError: The JWT was found to be invalid, the message will contain
details.
"""
Expand Down Expand Up @@ -147,10 +144,9 @@ def create_custom_token(self, uid, developer_claims=None):
Raises:
ValueError: If input parameters are invalid.
"""
credential = self._app.options.credential
if not isinstance(credential, CertificateCredential):
if not isinstance(self._app.credential, credentials.Certificate):
raise ValueError(
'Must initialize Firebase App with a certificate credential'
'Must initialize Firebase App with a certificate credential '
'to call create_custom_token().')

if developer_claims is not None:
Expand All @@ -176,8 +172,8 @@ def create_custom_token(self, uid, developer_claims=None):

now = int(time.time())
payload = {
'iss': credential.service_account_email,
'sub': credential.service_account_email,
'iss': self._app.credential.service_account_email,
'sub': self._app.credential.service_account_email,
'aud': self.FIREBASE_AUDIENCE,
'uid': uid,
'iat': now,
Expand All @@ -187,7 +183,7 @@ def create_custom_token(self, uid, developer_claims=None):
if developer_claims is not None:
payload['claims'] = developer_claims

return jwt.encode(payload, credential.signer)
return jwt.encode(payload, self._app.credential.signer)

def verify_id_token(self, id_token):
"""Verifies the signature and data for the provided JWT.
Expand All @@ -202,22 +198,21 @@ def verify_id_token(self, id_token):
A dict consisting of the key-value pairs parsed from the decoded JWT.

Raises:
ValueError: The app was not initialized with a CertificateCredential
ValueError: The app was not initialized with a credentials.Certificate instance.
AppIdenityError: The JWT was found to be invalid, the message will
contain details.
"""
if not id_token or not isinstance(id_token, basestring):
raise ValueError('Illegal ID token provided: {0}. ID token '
'must be a non-empty string.'.format(id_token))

credential = self._app.options.credential
try:
project_id = credential.project_id
project_id = self._app.credential.project_id
except AttributeError:
project_id = os.environ.get(GCLOUD_PROJECT_ENV_VAR)

if not project_id:
raise ValueError('Must initialize app with a CertificateCredential '
raise ValueError('Must initialize app with a credentials.Certificate '
'or set your Firebase project ID as the '
'GCLOUD_PROJECT environment variable to call '
'verify_id_token().')
Expand Down Expand Up @@ -281,76 +276,3 @@ def verify_id_token(self, id_token):
audience=project_id,
kid=header.get('kid'),
http=_http)


class Credential(object):
"""Provides OAuth2 access tokens for accessing Firebase services.
"""

def get_access_token(self, force_refresh=False):
"""Fetches a Google OAuth2 access token using this credential instance.

Args:
force_refresh: A boolean value indicating whether to fetch a new token
or use a cached one if available.
"""
raise NotImplementedError

def get_credential(self):
"""Returns the credential instance used for authentication."""
raise NotImplementedError


class CertificateCredential(Credential):
"""A Credential initialized from a JSON keyfile."""

def __init__(self, file_path):
"""Initializes a credential from a certificate file.

Parses the specified certificate file (service account file), and
creates a credential instance from it.

Args:
file_path: Path to a service account certificate file.

Raises:
IOError: If the specified file doesn't exist or cannot be read.
ValueError: If an error occurs while parsing the file content.
"""
super(CertificateCredential, self).__init__()
# TODO(hkj): Clean this up once we are able to take a dependency
# TODO(hkj): on latest oauth2client.
with open(file_path) as json_keyfile:
json_data = json.load(json_keyfile)
self._project_id = json_data.get('project_id')
try:
self._signer = crypt.Signer.from_string(
json_data.get('private_key'))
except Exception as error:
err_type, err_value, err_traceback = sys.exc_info()
err_message = 'Failed to parse the private key string: {0}'.format(
error)
raise ValueError, (err_message, err_type, err_value), err_traceback
self._service_account_email = json_data.get('client_email')
self._g_credential = client.GoogleCredentials.from_stream(file_path)

@property
def project_id(self):
return self._project_id

@property
def signer(self):
return self._signer

@property
def service_account_email(self):
return self._service_account_email

def get_access_token(self, force_refresh=False):
if force_refresh:
self._g_credential.refresh(httplib2.Http())
token_info = self._g_credential.get_access_token()
return token_info.access_token

def get_credential(self):
return self._g_credential
Loading