android login

1 parent 033d3fe8
......@@ -23,10 +23,8 @@ class PartnerModel(NamaModel):
fax = Column(String(16))
mobile = Column(String(16))
website = Column(String(64))
# pic = Column(String(16))
# pic_mobile = Column(String(16))
# pic_email = Column(String(16))
# pic_jabatan = Column(String(16))
is_vendor = Column(SmallInteger, nullable=False, )
is_customer = Column(SmallInteger, nullable=False, )
@classmethod
def query_email(cls, email):
......@@ -43,8 +41,7 @@ class Partner(Base, PartnerModel):
kecamatan = Column(String(128))
kota = Column(String(128))
provinsi = Column(String(128))
is_vendor = Column(SmallInteger, nullable=False, )
is_customer = Column(SmallInteger, nullable=False, )
# bank = Column(String(16))
# bank_accnt = Column(String(16))
# user_id = Column(Integer, ForeignKey(User.id), nullable=True) # referensi ke login
......
Generic single-database configuration.
\ No newline at end of file
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
"""Upgrade schema."""
${upgrades if upgrades else "pass"}
def downgrade() -> None:
"""Downgrade schema."""
${downgrades if downgrades else "pass"}
......@@ -155,7 +155,7 @@
const value = document.cookie;
const parts = value.split(`g_state=`);
if (parts.length === 2) {
document.cookie = "max-age=0";
// document.cookie = "max-age=0";
}
}
......
......@@ -23,6 +23,7 @@ import os
import re
from datetime import timedelta, datetime
from importlib import import_module
from pyramid.request import Response
import colander
from deform import widget, Form, ValidationFailure, Button
......@@ -121,17 +122,72 @@ class Oauth2ParseExc(Exception):
class Oauth2UserExc(Exception):
"""Error User Found"""
from google.oauth2 import id_token
from google.auth.transport import requests
def verify_android_token(token, web_client_id):
"""
Verifies a Google ID token from an Android client on the backend.
Args:
token (str): The ID token string received from the Android app.
web_client_id (str): The Client ID for your *web application* in
Google Cloud Console.
Returns:
dict: The decoded user information (claims) if the token is valid.
Raises: ValueError if the token is invalid or unverified.
"""
try:
# Create a request object for making HTTP requests to Google's servers
request = requests.Request()
# Verify the token against Google's public keys
# The function automatically checks the token's signature, expiry,
# and if it was issued by accounts.google.com.
id_info = id_token.verify_oauth2_token(
token,
request,
web_client_id
)
# Optional: Check if the token belongs to a specific Google Workspace domain (if required)
# if id_info.get('hd') != 'yourdomain.com':
# raise ValueError('Not a valid hosted domain.')
# Extract user information
user_id = id_info['sub']
email = id_info.get('email')
print(f"Token verified for User ID: {user_id}, Email: {email}")
return id_info
except ValueError as e:
# Invalid token (e.g., signature mismatch, expired, wrong audience)
print(f"Invalid token: {e}")
raise
def oauth2_login(request, params=None):
provider_name = params and params["provider_name"] or request.params["provider_name"]
if provider_name == "google":
from .base_google import googlesignin
try:
id_info = googlesignin(request, params)
except Exception as e:
raise Oauth2ParseExc(str(e))
client_platform = params and params.get("platform") or request.params.get("platform") or "web"
web_client_id = request.google_signin_client_id
if client_platform == "android":
id_info = verify_android_token(
params and params["id_token"] or request.params["id_token"],
web_client_id
)
else:
from .base_google import googlesignin
try:
id_info = googlesignin(request, params)
except Exception as e:
raise Oauth2ParseExc(str(e))
request.session["id_info"] = id_info
else:
id_info = None
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!