I am currently developing an android chat app. I am very new to Android Studio, JWT Token Authorization, and Django Rest Framework. Right now I am having issue to work on the Django side.
So basically I was setting up a login page from my Android, and I want it to login using phone number and password as the needed credentials. However, I also want to use JWT Token Auth to make my application more secure.
Currently I have my project urls.py pointing to one of the JWT Token API
urls.py
from django.contrib import admin
from django.urls import path,include
from django.conf.urls import include, url
from rest_framework_simplejwt import views as jwt_views
urlpatterns = [
path('admin/', admin.site.urls),
path('account/',include('restaccount.urls')) ,
path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]
This would lead to the server page which was
*PS : The phone number fields should be the default username field..(I have made some trial modifications on my code prior I post this).
I also have set up a models that was inherit from AbstractUser
models.py
class RegisterUser(AbstractUser):
phone_number = PhoneField(name='phone_number',unique=True)
birthday = models.DateField(name ='birthday',null= True)
nickname = models.CharField(max_length=100,name = 'nickname')
def __str__(self):
return self.phone_number
Currently I have tried to make a lot of modifications to my model, like :
change username = None
REQUIRED_FIELDS = []
USERNAME_FIELDS = 'phone_number'
I realize that the Token Obtain Pair View is following the Django Administration page in terms of the information that you needed (username and password).
However when I modified, I try to create superuser and try to login too Django Admin with my modified data..But I still cannot log in.. Also, I try to get token from the superuser that I have made, but it will response in "detail": "No active account found with the given credentials"
Can somebody enlighten me of the steps that I should take now?? I have tried to look for solutions but none of them solve my problem
Here's the point TLDR:
I want my app to Login using phone number and password and want to use JWT Token Auth to make it secure.
I realize the ObtainTokenPair view follows Django Admin credentials, so I have tried to modify my backend to be "log in" using phone number and password.
After I modified, I can't login to Django Admin and cannot get token with the superuser I created.
Here some of the related file attach:
Settings.py
"""
Django settings for androidapp project.
Generated by 'django-admin startproject' using Django 3.0.7.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '6qdk058^8b2#-pnw!cr1pbd(sao)vj+v69&4874zjh95xu7pg)'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['172.31.120.211',]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'restaccount',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'androidapp.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'androidapp.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'Orbital',
'USER' :'SomeUser',
'PASSWORD':'Pass',
'HOST' : 'localhost',
'PORT' : '',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = False
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
AUTH_USER_MODEL = 'restaccount.RegisterUser'
#FORMAT FOR DATE INPUT
DATE_INPUT_FORMATS = ('%d-%m-%Y', '%d/%m/%Y', '%d/%m/%y', '%d %b %Y',
'%d %b, %Y', '%d %b %Y', '%d %b, %Y', '%d %B, %Y',
'%d %B %Y')
#Format for date-time input format
DATETIME_INPUT_FORMATS = ('%d/%m/%Y %H:%M:%S', '%d/%m/%Y %H:%M', '%d/%m/%Y',
'%d/%m/%y %H:%M:%S', '%d/%m/%y %H:%M', '%d/%m/%y',
'%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d')
# Adding REST_FRAMEWORK SETTING WITH JWT AUTHENTICATION
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
# AUTHENTICATION_BACKENDS = (
# 'django.contrib.auth.backends.ModelBackend',
# 'restaccount.backends.UserBackend'
# )
RegisterUserManager inside models.py
class RegisterUserManager(BaseUserManager):
def create_user(self, phone_number,password, **extra_fields):
if not phone_number:
raise ValueError('The phone number must be set')
user = self.model(
phone_number=phone_number,
password = password,
**extra_fields)
user.save()
return user
def create_superuser(self,phone_number,password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
# print(phone_number)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(phone_number, password,**extra_fields)
Its quite difficult to pin point the bug without getting hands-on to the actual project. I can't not find the bug or fix your project. that you have to do on your own. But I can surely share what I think would help you avoid bug and fix your project.
what I could understand
you want a custom user model
your want to use jwt authentication
so, let's begin. User model and authentication are two different things. create Custom User model first.
firstly, remove all users from database
create Custom User model following this 'A full example' exactly (check by creating superuser if custom user model is working properly, if not that means you missed something try again)
If you have successfully created custom user model that means you now have substituted 'username' with 'phone number' (in your case)
for authentication you can use custom authentication or as you tried you can use existing packages. Configure it to act as default authentication backend.
your choice of authentication package should take username and password, check if there is a user match those credentials create a token and return that token. you don't need to modify the authentication process you just provide username field(phone number) and password. Now here you might need to do something like
{username: phone_number, password: password}
because your authentication package might not support custom user.
hope it helps.
This is the code that I have:
final FirebaseRemoteConfig config = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
config.setConfigSettings(configSettings);
config.fetch(0).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
// After config data is successfully fetched, it must be activated before newly fetched
// values are returned.
config.activateFetched();
final String playStoreVersionCode = FirebaseRemoteConfig.getInstance().getString(
"android_latest_version_code");
} else {
Utils.appendLog("playStoreVersionCode Error fetching latest params ", "E", Constants.TIMELINE);
}
}
});
Now I want to increment my parameter, and I saw that from March there is a REST API in order to update parameters:
https://firebase.google.com/docs/remote-config/api-overview
https://firebase.google.com/docs/remote-config/use-config-rest
But I don't really understand the tutorial.
Why do I need to use curl? is this really necessary like in the use-config-rest link?
curl --compressed -D headers -H "Authorization: Bearer token" -X GET https://firebaseremoteconfig.googleapis.com/v1/projects/my-project-id/remoteConfig -o filename
And the quickstart just shows an example on how to fetch the data, not change it:
https://github.com/firebase/quickstart-android/blob/master/config/app/src/main/java/com/google/samples/quickstart/config/MainActivity.java
Remote Config parameters aren't meant to be changed from client code. They're only meant to be read on the client. If you want to change thing programmatically, you're supposed to do that from a server your control.
If you want a read/write some persisted value in your app, don't use Remote Config. Use Realtime Database or Firestore instead.
The reason for showing curl commands in the documentation is to illustrate how to make the HTTP request using a command that many people are familiar with. You can make the HTTP request any way you want, as long as you follow the specification.
I want to verify user profile from Google Authorization code sent by android client, to do that, we have to download client_secrets.json and put it inside our rails app. Just like this tutorial https://developers.google.com/identity/protocols/OAuth2WebServer
But when I try to follow this step
require 'google/apis/drive_v2'
require 'google/api_client/client_secrets'
client_secrets = Google::APIClient::ClientSecrets.load
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
:redirect_uri => 'http://www.example.com/oauth2callback',
:additional_parameters => {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true" # incremental auth
}
)
Rails throws an error said "No client_secrets.json filename supplied and/or could not be found in search path."
The errors shows up even though I have insert client_secrets.json inside config/client_secrets.json
Do you know what's the problem or what's the alternative for this solution, thank you.
It seems, ClientSecrets.load accepts optional argument which is config filename path. So I believe it's okay if you specify filename directly:
Google::APIClient::ClientSecrets.load("#{Rails.root}/config/client_secrets.json")
I have an Android app which let the users to add content to my server. Each user should have an account on this server.
The app communicate with the server through a simple API.
I want to use user's gmail address as the usermame and the auth_token obtained form AccountManager as a password.
The problem is that the auth_token is not always the same so it cannot be used as a normal password.
Normally the server receive user's gmail address and the auth_token and it should check if the token is valid for received gmail address. The problem is that this is not possible: google do not have any method which will let me check this.
Do you know how can I let my users to log in on my server using their gmail address but without prompting them to enter a password?
I want to have something like "Login with Google" from Stackoverflow but I don't understand how can I make sure that the received token on my server is correct and not a fake one.
Thank you
There should be a way of sending the auth_token to google and retrieving information like the google user_ID since you can retrieve those infos :
{
"audience":"8819981768.apps.googleusercontent.com",
"user_id":"123456789",
"scope":"https://gdata.youtube.com",
"expires_in":436
}
by making a POST from your android app on : https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=1/fFBGRNJru1FQd44AzqT3Zg with the access token ... It's even indicated in https://developers.google.com/youtube/2.0/developers_guide_protocol_oauth2#OAuth2_Authentication The problem is that when you want to do it from your php server you'll have to send it in SSL because google requires it ... Here is my code :
<?php
if( isset($_POST['authToken'])){
header('Content-Type: text/plain');
$name = 'www.googleapis.com';//nom du site
$data = "access_token='".$_POST['authToken']."' ";
$envoi = "POST /oauth2/v1/tokeninfo HTTP/1.1\r\n";
$envoi .= "Host: ".$name."\r\n";
$envoi .= "Connection: Close\r\n";
$envoi .= "Content-type: application/x-www-form-urlencoded\r\n";
$envoi .= "Content-Length: ".strlen($data)."\r\n\r\n";
$envoi .= $data."\r\n";
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($socket < 0){
die('FATAL ERROR: socket_create() : " '.socket_strerror($socket).' "');
}
if (socket_connect($socket,gethostbyname($name),80) < 0){
die('FATAL ERROR: socket_connect()');
}
if(($int = socket_write($socket, $envoi, strlen($envoi))) === false){
die('FATAL ERROR: socket_write() failed, '.$int.' characters written');
}
$reception = '';
while($buff = socket_read($socket, 2000)){
$reception.=$buff;
}
echo $reception;
socket_close($socket);
}
?>
it just needs to be adapted to SSL and it should work to retrieve the google user_id ! After what you can compare it with the one in your database and the logg is done !
By the way, could you share your code with us (using your method with auth_token for contacts) ? or send it to fakeclark#live.com THANKS ;-)
I want to send data from my Android app to my server.
On the client-side, meaning the application,I can create the JSON object and send it to the server.
The problem is, I don't know how to 'handle' it on the server side.All I want my server to do is to receive the JSON, parse it, and show it to me.That's it.
I know it's pretty vague question, but I don't really know where to start here, and would love if anyone could show me a complete tutorial.
Thanks!
Use PHP and json_decode()
http://php.net/manual/en/function.json-decode.php
Here a quick example how to handle the data:
// get json
$input = json_decode($_GET["json"]);
// get values
$firstname = $input->firstName;
$surename = $input->lastName;
$age = intval($input->age);
// check values
if (isset($firstname) && !empty($firstname) &&
isset($surename) && !empty($surename) &&
isset($age) && is_numeric($age))
{
// do something
echo "Hello ".htmlspecialchars($firstname)." ".htmlspecialchars($surename)."!<br>";
echo "You are $age years old! Wow.";
}
else
{
echo "Some values are missing or incorrect";
}
I used the GET parameter in this example. If you have larger data, use POST instead of GET.
Example:
URL: http://localhost/test/index.php?json={ "firstName" : "John","lastName" : "Doe","age" : 23 }
Output: Hello John Doe!
You are 23 years old! Wow.
But: Make sure you encode the JSON data at your application. In my example the browser does it.
// Some groovy code to dump an incoming request
import com.sun.net.httpserver.*;
HttpServer server = HttpServer.create(new InetSocketAddress(2228),0)
server.createContext('/', { HttpExchange exchange ->
println 'got a request'
println 'requestHeaders '+exchange.requestHeaders
println 'requestBody '+exchange.requestBody.text
exchange.sendResponseHeaders(200,0);
exchange.responseBody.write('hello from groovy land.'.bytes)
exchange.responseBody.close();
println 'all done'
} as HttpHandler)
server.start();
You can even use some shell script CGI file on the server. Here's a sample returning some fixed data to a JSONP request for testing something.
#!/bin/bash
#
# Handle a JSONP request for data returning a fake queue status result.
read -r -d '' DATA <<'EOF'
{
name: "TESTHOST",
status: "running",
items: [
{id:"4",status:"failed",title:"anadin map 2",user:"pat",progress:100},
{id:"2",status:"running",title:"silicon map",user:"tim",progress:52},
{id:"3",status:"queued",title:"anadin map",user:"pat",progress:0},
{id:"6",status:"queued",title:"neon calibration",user:"ian",progress:0}
]
}
EOF
CB=$(echo $QUERY_STRING | sed -n 's/.*jsoncallback=\([^&]*\).*$/\1/p')
DATA=${DATA/52/$(expr $RANDOM % 100)}
DATA="${CB}(${DATA});"
echo -e "content-type: application/json\r"
echo -e "content-length: ${#DATA}\r"
echo -e "x-test: $CB\r"
echo -e "\r"
echo "$DATA"
Substitute some parsing of the request data and return as appropriate.