Skip to content

Commit

Permalink
Support github backend
Browse files Browse the repository at this point in the history
  • Loading branch information
saxtouri committed Jul 13, 2017
1 parent 0ed1a4e commit 4fb88a9
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 0 deletions.
4 changes: 4 additions & 0 deletions example/internal_attributes.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ attributes:
saml: [postaladdress]
displayname:
openid: [nickname]
github: [login]
saml: [displayName]
edupersontargetedid:
facebook: [id]
github: [id]
openid: [sub]
saml: [eduPersonTargetedID]
givenname:
Expand All @@ -15,10 +17,12 @@ attributes:
saml: [givenName]
mail:
facebook: [email]
github: [email]
openid: [email]
saml: [email, emailAdress, mail]
name:
facebook: [name]
github: [name]
openid: [name]
saml: [cn]
surname:
Expand Down
29 changes: 29 additions & 0 deletions example/plugins/backends/github_backend.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module: satosa.backends.github.GitHubBackend
name: github
config:
authz_page: github/auth/callback
base_url: https://www.example.org
client_config:
client_id: 123d45g678
client_secret: 3h4h5j67l5k3l3l
scope: [user]
response_type: code
allow_#: false
server_info: {
authorization_endpoint: 'http://github.com/#/oauth/authorize',
token_endpoint: 'https://github.com/#/oauth/access_token',
user_info: 'https://api.github.com/user'
}
entity_info:
organization:
display_name:
- ["GitHub", "en"]
name:
- ["GitHub", "en"]
url:
- ["https://github.com/", "en"]
ui_info:
description:
- ["GitHub oauth", "en"]
display_name:
- ["GitHub", "en"]
111 changes: 111 additions & 0 deletions src/satosa/backends/github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
OAuth backend for LinkedIn
"""
import json
import logging
import requests

from oic.utils.authn.authn_context import UNSPECIFIED
from oic.oauth2.consumer import stateID
from oic.oauth2.message import AuthorizationResponse

from satosa.backends.oauth import _OAuthBackend
from ..internal_data import InternalResponse
from ..internal_data import AuthenticationInformation
from ..response import Redirect
from ..util import rndstr

logger = logging.getLogger(__name__)


class GitHubBackend(_OAuthBackend):
"""GitHub OAuth 2.0 backend"""

def __init__(self, outgoing, internal_attributes, config, base_url, name):
"""GitHub backend constructor
:param outgoing: Callback should be called by the module after the
authorization in the backend is done.
:param internal_attributes: Mapping dictionary between SATOSA internal
attribute names and the names returned by underlying IdP's/OP's as
well as what attributes the calling SP's and RP's expects namevice.
:param config: configuration parameters for the module.
:param base_url: base url of the service
:param name: name of the plugin
:type outgoing:
(satosa.context.Context, satosa.internal_data.InternalResponse) ->
satosa.response.Response
:type internal_attributes: dict[string, dict[str, str | list[str]]]
:type config: dict[str, dict[str, str] | list[str] | str]
:type base_url: str
:type name: str
"""
config.setdefault('response_type', 'code')
config['verify_accesstoken_state'] = False
super().__init__(
outgoing, internal_attributes, config, base_url, name, 'github',
'id')

def start_auth(self, context, internal_request, get_state=stateID):
"""
:param get_state: Generates a state to be used in authentication call
:type get_state: Callable[[str, bytes], str]
:type context: satosa.context.Context
:type internal_request: satosa.internal_data.InternalRequest
:rtype satosa.response.Redirect
"""
oauth_state = get_state(self.config["base_url"], rndstr().encode())
context.state[self.name] = dict(state=oauth_state)

request_args = dict(
client_id=self.config['client_config']['client_id'],
redirect_uri=self.redirect_url,
state=oauth_state,
allow_#=self.config.get('allow_#', False))
scope = ' '.join(self.config['scope'])
if scope:
request_args['scope'] = scope

cis = self.consumer.construct_AuthorizationRequest(
request_args=request_args)
return Redirect(cis.request(self.consumer.authorization_endpoint))

def auth_info(self, requrest):
return AuthenticationInformation(
UNSPECIFIED, None,
self.config['server_info']['authorization_endpoint'])

def _authn_response(self, context):
state_data = context.state[self.name]
aresp = self.consumer.parse_response(
AuthorizationResponse, info=json.dumps(context.request))
self._verify_state(aresp, state_data, context.state)
url = self.config['server_info']['token_endpoint']
data = dict(
code=aresp['code'],
redirect_uri=self.redirect_url,
client_id=self.config['client_config']['client_id'],
client_secret=self.config['client_secret'], )
headers = {'Accept': 'application/json'}

r = requests.post(url, data=data, headers=headers)
response = r.json()
if self.config.get('verify_accesstoken_state', True):
self._verify_state(response, state_data, context.state)

user_info = self.user_information(response["access_token"])
auth_info = self.auth_info(context.request)
internal_response = InternalResponse(auth_info=auth_info)
internal_response.attributes = self.converter.to_internal(
self.external_type, user_info)
internal_response.user_id = user_info[self.user_id_attr]
del context.state[self.name]
return self.auth_callback_func(context, internal_response)

def user_information(self, access_token):
url = self.config['server_info']['user_info']
headers = {'Authorization': 'token {}'.format(access_token)}
r = requests.get(url, headers=headers)
ret = r.json()
ret['id'] = str(ret['id'])
return r.json()

0 comments on commit 4fb88a9

Please # to comment.