def get_login_url(request):
    if request.session.has_key(
            'pkce_verifier') is False or request.session.has_key(
                'code_challenge') is False:
        code_verifier = pkce.generate_code_verifier(length=128)
        code_challenge = pkce.get_code_challenge(code_verifier)
        request.session['pkce_verifier'] = code_verifier
        request.session['code_challenge'] = code_challenge
    redirect_url = request.build_absolute_uri(reverse("dashboard"))
    login_url = f"{settings.FUSION_AUTH_BASE_URL}/oauth2/authorize?client_id={settings.FUSION_AUTH_APP_ID}&redirect_uri={redirect_url}&response_type=code&code_challenge={request.session['code_challenge']}&code_challenge_method=S256"
    login_url = login_url.format(
        settings.FUSION_AUTH_BASE_URL,
        settings.FUSION_AUTH_APP_ID,
    )
    return login_url
예제 #2
0
def get_youtube_auth_url():
    state = generate_state()
    code_challenge = pkce.get_code_challenge(code_verifier)
    endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
    payload = {
        "client_id": app_secrets.youtube_client_id,
        "redirect_uri": "http://127.0.0.1:5000",
        "response_type": "code",
        "scope": "https://www.googleapis.com/auth/youtube.readonly",
        "code_challenge": code_challenge,
        "code_challenge_method": "S256",
        "state": state,
        "access_type": "offline"
    }
    res = requests.head(endpoint, params=payload)
    print(res.url)
    return res.url
예제 #3
0
    async def _oauth_authenticate(self) -> Tuple[str, int]:

        async with ClientSession() as session:
            # retrieve authentication page
            _LOGGER.debug("Retrieving authentication page")
            resp, html = await self.request(
                method="get",
                returns="text",
                url=OAUTH_AUTHORIZE_URI,
                websession=session,
                headers={
                    "redirect": "follow",
                },
                params={
                    "client_id": OAUTH_CLIENT_ID,
                    "code_challenge": get_code_challenge(self._code_verifier),
                    "code_challenge_method": "S256",
                    "redirect_uri": OAUTH_REDIRECT_URI,
                    "response_type": "code",
                    "scope": "MyQ_Residential offline_access",
                },
                login_request=True,
            )

            # Scanning returned web page for required fields.
            _LOGGER.debug("Scanning login page for fields to return")
            soup = BeautifulSoup(html, "html.parser")

            # Go through all potential forms in the page returned. This is in case multiple forms are returned.
            forms = soup.find_all("form")
            data = {}
            for form in forms:
                have_email = False
                have_password = False
                have_submit = False
                # Go through all the input fields.
                for field in form.find_all("input"):
                    if field.get("type"):
                        # Hidden value, include so we return back
                        if field.get("type").lower() == "hidden":
                            data.update({
                                field.get("name", "NONAME"):
                                field.get("value", "NOVALUE")
                            })
                        # Email field
                        elif field.get("type").lower() == "email":
                            data.update(
                                {field.get("name", "Email"): self.username})
                            have_email = True
                        # Password field
                        elif field.get("type").lower() == "password":
                            data.update({
                                field.get("name", "Password"):
                                self.__credentials.get("password")
                            })
                            have_password = True
                        # To confirm this form also has a submit button
                        elif field.get("type").lower() == "submit":
                            have_submit = True

                # Confirm we found email, password, and submit in the form to be submitted
                if have_email and have_password and have_submit:
                    break

                # If we're here then this is not the form to submit.
                data = {}

            # If data is empty then we did not find the valid form and are unable to continue.
            if len(data) == 0:
                _LOGGER.debug("Form with required fields not found")
                raise RequestError(
                    "Form containing fields for email, password and submit not found."
                    "Unable to continue login process.")

            # Perform login to MyQ
            _LOGGER.debug("Performing login to MyQ")
            resp, _ = await self.request(
                method="post",
                returns="response",
                url=resp.url,
                websession=session,
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Cookie": resp.cookies.output(attrs=[]),
                },
                data=data,
                allow_redirects=False,
                login_request=True,
            )

            # We're supposed to receive back at least 2 cookies. If not then authentication failed.
            if len(resp.cookies) < 2:
                message = "Invalid MyQ credentials provided. Please recheck login and password."
                self._invalid_credentials = True
                _LOGGER.debug(message)
                raise InvalidCredentialsError(message)

            # Intercept redirect back to MyQ iOS app
            _LOGGER.debug("Calling redirect page")
            resp, _ = await self.request(
                method="get",
                returns="response",
                url=f"{OAUTH_BASE_URI}{resp.headers['Location']}",
                websession=session,
                headers={
                    "Cookie": resp.cookies.output(attrs=[]),
                },
                allow_redirects=False,
                login_request=True,
            )

            # Retrieve token
            _LOGGER.debug("Getting token")
            redirect_url = f"{OAUTH_BASE_URI}{resp.headers['Location']}"

            resp, data = await self.request(
                returns="json",
                method="post",
                url=OAUTH_TOKEN_URI,
                websession=session,
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                },
                data={
                    "client_id":
                    OAUTH_CLIENT_ID,
                    "client_secret":
                    OAUTH_CLIENT_SECRET,
                    "code":
                    parse_qs(urlsplit(redirect_url).query).get("code", ""),
                    "code_verifier":
                    self._code_verifier,
                    "grant_type":
                    "authorization_code",
                    "redirect_uri":
                    OAUTH_REDIRECT_URI,
                    "scope":
                    parse_qs(urlsplit(redirect_url).query).get(
                        "code", "MyQ_Residential offline_access"),
                },
                login_request=True,
            )

            token = f"{data.get('token_type')} {data.get('access_token')}"
            try:
                expires = int(data.get("expires_in", DEFAULT_TOKEN_REFRESH))
            except ValueError:
                _LOGGER.debug(
                    f"Expires {data.get('expires_in')} received is not an integer, using default."
                )
                expires = DEFAULT_TOKEN_REFRESH * 2

        if expires < DEFAULT_TOKEN_REFRESH * 2:
            _LOGGER.debug(
                f"Expires {expires} is less then default {DEFAULT_TOKEN_REFRESH}, setting to default instead."
            )
            expires = DEFAULT_TOKEN_REFRESH * 2

        return token, expires
예제 #4
0
def test_get_code_challenge_too_short_or_too_long(length):
    verifier = ''.join(random.choices(string.ascii_letters, k=length))
    with pytest.raises(ValueError):
        pkce.get_code_challenge(verifier)
예제 #5
0
def test_get_code_challenge():
    abc = 'abcdefghijklmnopqrstuvwxyz'
    challenge = pkce.get_code_challenge(abc + abc.upper())
    assert challenge == 'OWQpS2ZGE3mNGkd-uK0CEYtI0MVzjEJ2EyAvLtEjtfE'
예제 #6
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.client_id = credentials.create_client_id()
     self.client_secret = credentials.create_client_secret()
     self.code_verifier = credentials.create_code_verifier()
     self.code_challenge = pkce.get_code_challenge(self.code_verifier)
예제 #7
0
AUTH0_CALLBACK_URL = env.get(constants.AUTH0_CALLBACK_URL)
AUTH0_CLIENT_ID = env.get(constants.AUTH0_CLIENT_ID)
AUTH0_CLIENT_SECRET = env.get(constants.AUTH0_CLIENT_SECRET)
AUTH0_DOMAIN = env.get(constants.AUTH0_DOMAIN)
AUTH0_BASE_URL = 'https://' + AUTH0_DOMAIN
AUTH0_AUDIENCE = env.get(constants.AUTH0_AUDIENCE)
MY_URL = env.get('MY_URL')


app = Flask(__name__, static_url_path='/public', static_folder='./public')
app.secret_key = constants.SECRET_KEY
app.debug = True

code_verifier = pkce.generate_code_verifier(length=128)
code_challenge = pkce.get_code_challenge(code_verifier)

@app.errorhandler(Exception)
def handle_auth_error(ex):
    response = jsonify(message=str(ex))
    response.status_code = (ex.code if isinstance(ex, HTTPException) else 500)
    return response


oauth = OAuth(app)

auth0 = oauth.register(
    'auth0',
    client_id=AUTH0_CLIENT_ID,
    client_secret=AUTH0_CLIENT_SECRET,
    api_base_url=AUTH0_BASE_URL + '/',