You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

216 lines
5.8 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe, os
  5. import httplib2
  6. import json
  7. from werkzeug.utils import redirect
  8. import frappe.utils
  9. no_cache = True
  10. def get_context(context):
  11. # get settings from site config
  12. context["title"] = "Login"
  13. for provider in ("google", "github", "facebook"):
  14. if get_oauth_keys(provider):
  15. context["{provider}_login".format(provider=provider)] = get_oauth2_authorize_url(provider)
  16. context["social_login"] = True
  17. return context
  18. oauth2_providers = {
  19. "google": {
  20. "flow_params": {
  21. "name": "google",
  22. "authorize_url": "https://accounts.google.com/o/oauth2/auth",
  23. "access_token_url": "https://accounts.google.com/o/oauth2/token",
  24. "base_url": "https://www.googleapis.com",
  25. },
  26. "redirect_uri": "/api/method/frappe.templates.pages.login.login_via_google",
  27. "auth_url_data": {
  28. "scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
  29. "response_type": "code"
  30. },
  31. # relative to base_url
  32. "api_endpoint": "oauth2/v2/userinfo"
  33. },
  34. "github": {
  35. "flow_params": {
  36. "name": "github",
  37. "authorize_url": "https://github.com/login/oauth/authorize",
  38. "access_token_url": "https://github.com/login/oauth/access_token",
  39. "base_url": "https://api.github.com/"
  40. },
  41. "redirect_uri": "/api/method/frappe.templates.pages.login.login_via_github",
  42. # relative to base_url
  43. "api_endpoint": "user"
  44. },
  45. "facebook": {
  46. "flow_params": {
  47. "name": "facebook",
  48. "authorize_url": "https://www.facebook.com/dialog/oauth",
  49. "access_token_url": "https://graph.facebook.com/oauth/access_token",
  50. "base_url": "https://graph.facebook.com"
  51. },
  52. "redirect_uri": "/api/method/frappe.templates.pages.login.login_via_facebook",
  53. "auth_url_data": {
  54. "display": "page",
  55. "response_type": "code",
  56. "scope": "email,user_birthday"
  57. },
  58. # relative to base_url
  59. "api_endpoint": "me"
  60. }
  61. }
  62. def get_oauth_keys(provider):
  63. """get client_id and client_secret from database or conf"""
  64. # try conf
  65. keys = frappe.conf.get("{provider}_login".format(provider=provider))
  66. if not keys:
  67. # try database
  68. social = frappe.doc("Social Login Keys", "Social Login Keys")
  69. keys = {}
  70. for fieldname in ("client_id", "client_secret"):
  71. value = social.fields.get("{provider}_{fieldname}".format(provider=provider, fieldname=fieldname))
  72. if not value:
  73. keys = {}
  74. break
  75. keys[fieldname] = value
  76. return keys
  77. def get_oauth2_authorize_url(provider):
  78. flow = get_oauth2_flow(provider)
  79. # relative to absolute url
  80. data = { "redirect_uri": get_redirect_uri(provider) }
  81. # additional data if any
  82. data.update(oauth2_providers[provider].get("auth_url_data", {}))
  83. return flow.get_authorize_url(**data)
  84. def get_oauth2_flow(provider):
  85. from rauth import OAuth2Service
  86. # get client_id and client_secret
  87. params = get_oauth_keys(provider)
  88. # additional params for getting the flow
  89. params.update(oauth2_providers[provider]["flow_params"])
  90. # and we have setup the communication lines
  91. return OAuth2Service(**params)
  92. def get_redirect_uri(provider):
  93. redirect_uri = oauth2_providers[provider]["redirect_uri"]
  94. return frappe.utils.get_url(redirect_uri)
  95. @frappe.whitelist(allow_guest=True)
  96. def login_via_google(code):
  97. login_via_oauth2("google", code, decoder=json.loads)
  98. @frappe.whitelist(allow_guest=True)
  99. def login_via_github(code):
  100. login_via_oauth2("github", code)
  101. @frappe.whitelist(allow_guest=True)
  102. def login_via_facebook(code):
  103. login_via_oauth2("facebook", code)
  104. def login_via_oauth2(provider, code, decoder=None):
  105. flow = get_oauth2_flow(provider)
  106. args = {
  107. "data": {
  108. "code": code,
  109. "redirect_uri": get_redirect_uri(provider),
  110. "grant_type": "authorization_code"
  111. }
  112. }
  113. if decoder:
  114. args["decoder"] = decoder
  115. session = flow.get_auth_session(**args)
  116. api_endpoint = oauth2_providers[provider].get("api_endpoint")
  117. info = session.get(api_endpoint).json()
  118. if "verified_email" in info and not info.get("verified_email"):
  119. frappe.throw("{verify}: {provider}".format(
  120. verify=_("Error. Please verify your email with"),
  121. provider=provider.title()))
  122. login_oauth_user(info, provider=provider)
  123. def login_oauth_user(data, provider=None):
  124. user = data["email"]
  125. if not frappe.db.exists("User", user):
  126. create_oauth_user(data, provider)
  127. frappe.local.login_manager.user = user
  128. frappe.local.login_manager.post_login()
  129. # redirect!
  130. frappe.local.response["type"] = "redirect"
  131. frappe.local.response["location"] = "/app" if frappe.local.response.get('message') == 'Logged In' else "/"
  132. # because of a GET request!
  133. frappe.db.commit()
  134. def create_oauth_user(data, provider):
  135. if data.get("birthday"):
  136. from frappe.utils.dateutils import parse_date
  137. data["birthday"] = parse_date(data["birthday"])
  138. if isinstance(data.get("location"), dict):
  139. data["location"] = data.get("location").get("name")
  140. user = frappe.bean({
  141. "doctype":"User",
  142. "first_name": data.get("first_name") or data.get("given_name") or data.get("name"),
  143. "last_name": data.get("last_name") or data.get("family_name"),
  144. "email": data["email"],
  145. "gender": data.get("gender"),
  146. "enabled": 1,
  147. "new_password": frappe.generate_hash(data["email"]),
  148. "location": data.get("location"),
  149. "birth_date": data.get("birthday"),
  150. "user_type": "Website User",
  151. "user_image": data.get("picture") or data.get("avatar_url")
  152. })
  153. if provider=="facebook":
  154. user.doc.fields.update({
  155. "fb_username": data["username"],
  156. "fb_userid": data["id"],
  157. "user_image": "https://graph.facebook.com/{username}/picture".format(username=data["username"])
  158. })
  159. elif provider=="google":
  160. user.doc.google_userid = data["id"]
  161. elif provider=="github":
  162. user.doc.github_userid = data["id"]
  163. user.doc.github_username = data["login"]
  164. user.ignore_permissions = True
  165. user.get_controller().no_welcome_mail = True
  166. user.insert()