Compare commits

..
7 Commits
Author SHA1 Message Date
rizky_denianto 1baf543139 Merge pull request 'Menyelesaikan dasar fungsi plugin' (#1) from pengembangan into main
Reviewed-on: #1
2026-09-03 20:21:54 +07:00
rizky_denianto 524a27f54d Memperbaiki package.json
Penerbitan Plugin / persiapan (pull_request) Successful in 5s
Penerbitan Plugin / penerbitan (pull_request) Successful in 4m25s
2026-09-03 00:03:04 +07:00
rizky_denianto c0ec86185b Menyesuaikan keterangan dengan fungsi yang ada
Penerbitan Plugin / persiapan (pull_request) Successful in 5s
Penerbitan Plugin / penerbitan (pull_request) Failing after 2m7s
2026-09-02 23:51:05 +07:00
rizky_denianto caf005b82d Menyelesaikan plugin, siap ucob
Penerbitan Plugin / persiapan (pull_request) Successful in 5s
Penerbitan Plugin / penerbitan (pull_request) Failing after 1m29s
2026-09-02 23:13:23 +07:00
rizky_denianto 6f5478e9ab Penyesuaian penerbitan.yml 2026-09-02 01:12:56 +07:00
rizky_denianto 139d6f1256 Menambah pengunduhan rust 2026-09-02 00:23:42 +07:00
rizky_denianto e2808ca81e Menggunakan kembali kunci penerbitan dan menambah penerbitan peti cargo 2026-09-02 00:20:11 +07:00
22 changed files with 428 additions and 46 deletions

No files matched your search

+32 -2
View File
@@ -21,6 +21,21 @@ jobs:
runs-on: ubuntu-latest
needs: persiapan
steps:
- name: Unduh paket untuk membangun citra
run: |
apt update -y
sudo apt install -y libwebkit2gtk-4.1-dev \
build-essential \
curl \
wget \
file \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libudev-dev
apt clean -y
- name: Unduh program
uses: actions/checkout@v7
with:
@@ -28,10 +43,25 @@ jobs:
- name: Unduh pnpm
uses: pnpm/setup@v2
with:
version: 11
- name: Unggah program
- name: Penerbitan paket NPM
run: |
pnpm i
npm config set @${{ needs.persiapan.outputs.organisasi }}:registry=https://${{ vars.ALAMAT_GITEA }}/api/packages/${{ needs.persiapan.outputs.organisasi }}/npm/
npm config set -- '//${{ vars.ALAMAT_GITEA }}/api/packages/${{ needs.persiapan.outputs.organisasi }}/npm/:_authToken' "${{ secrets.GITEA_TOKEN }}"
npm config set -- '//${{ vars.ALAMAT_GITEA }}/api/packages/${{ needs.persiapan.outputs.organisasi }}/npm/:_authToken' "${{ secrets.KUNCI_PENERBITAN }}"
npm publish
rm -rf pnpm-lock.yaml .pnpm-store/
- name: Unduh Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
rustflags: "-A warnings"
- name: Penerbitan peti Cargo
run: |
cargo publish
env:
CARGO_REGISTRIES_GITEA_TOKEN: Bearer ${{ secrets.KUNCI_PENERBITAN }}
+3 -3
View File
@@ -2,7 +2,7 @@
name = "tauri-plugin-auth"
version = "0.1.0"
authors = [ "Rizky Denianto" ]
description = ""
description = "Program sederhana untuk interaksi dengan autentikasi aplikasi tauri di berbagai sistem operasi"
edition = "2024"
rust-version = "1.98.0"
exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
@@ -18,8 +18,8 @@ strip = "symbols"
debug = false
[package.metadata.platforms.support]
windows = { level = "full" }
linux = { level = "full" }
windows = { level = "none" }
linux = { level = "none" }
macos = { level = "none" }
android = { level = "full" }
ios = { level = "none" }
+2
View File
@@ -0,0 +1,2 @@
/build
/.tauri
+48
View File
@@ -0,0 +1,48 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.plugin.auth"
compileSdk = 36
defaultConfig {
minSdk = 21
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation(project(":tauri-android"))
implementation("androidx.credentials:credentials:1.7.0-alpha03")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")
implementation("com.google.android.libraries.identity.googleid:googleid:<latest version>")
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
+31
View File
@@ -0,0 +1,31 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
google()
}
resolutionStrategy {
eachPlugin {
switch (requested.id.id) {
case "com.android.library":
useVersion("8.0.2")
break
case "org.jetbrains.kotlin.android":
useVersion("1.8.20")
break
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
google()
}
}
include ':tauri-android'
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
+157
View File
@@ -0,0 +1,157 @@
package com.plugin.auth
import android.app.Activity
import android.content.Context
import android.util.Base64
import androidx.credentials.ClearCredentialStateRequest
import androidx.credentials.CredentialManager
import androidx.credentials.CustomCredential
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.exceptions.ClearCredentialException
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.NoCredentialException
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.security.SecureRandom
@InvokeArg
class KirimArgs {
var idKlien: String? = null
}
@TauriPlugin
class BluclasPlugin(private val activity: Activity) : Plugin(activity) {
private val credentialManager by lazy { CredentialManager.create(activity) }
private val scope = CoroutineScope(Dispatchers.Main)
private val idKlien: String? = null
@Command
fun masuk(invoke: Invoke) {
val args = invoke.parseArgs(KirimArgs::class.java)
idKlien = args.idKlien
if (idKlien.isNullOrEmpty()) {
invoke.reject("ID Klien belum disediakan")
return
}
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(idKlien)
.setNonce(generateSecureRandomNonce())
.build()
val request = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
scope.launch {
try {
val result = credentialManager.getCredential(
request = request,
context = activity
)
handleSignInResult(result, invoke)
} catch (e: NoCredentialException) {
if (args.filterByAuthorizedAccounts) {
signInWithAllAccounts(idKlien, invoke)
} else {
invoke.reject("Tidak ada kredensial Google yang tersedia: ${e.message}")
}
} catch (e: GetCredentialException) {
invoke.reject("Gagal masuk: ${e.message}")
}
}
}
@Command
fun keluar(invoke: Invoke) {
scope.launch {
try {
credentialManager.clearCredentialState(ClearCredentialStateRequest())
invoke.resolve()
} catch (e: ClearCredentialException) {
invoke.reject("Sign-out failed: ${e.message}")
}
}
}
private suspend fun signInWithAllAccounts(serverClientId: String, invoke: Invoke) {
val googleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(serverClientId)
.setNonce(generateSecureRandomNonce())
.build()
val request = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
try {
val result = credentialManager.getCredential(
request = request,
context = activity
)
handleSignInResult(result, invoke)
} catch (e: GetCredentialException) {
invoke.reject("Sign-in failed: ${e.message}")
}
}
private fun generateSecureRandomNonce(byteLength: Int = 32): String {
val randomBytes = ByteArray(byteLength)
SecureRandom().nextBytes(randomBytes)
return Base64.encodeToString(randomBytes, Base64.NO_WRAP or Base64.URL_SAFE or Base64.NO_PADDING)
}
private fun handleSignInResult(result: GetCredentialResponse, invoke: Invoke) {
val credential = result.credential
when (credential) {
is CustomCredential -> {
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
try {
val googleIdTokenCredential =
GoogleIdTokenCredential.createFrom(credential.data)
// NOTE: do not treat the user as signed in on the client
// alone — send idToken to your backend and verify it there
// before trusting it.
val ret = JSObject()
ret.put("idToken", googleIdTokenCredential.idToken)
ret.put("id", googleIdTokenCredential.id)
ret.put("displayName", googleIdTokenCredential.displayName)
ret.put("givenName", googleIdTokenCredential.givenName)
ret.put("familyName", googleIdTokenCredential.familyName)
ret.put(
"profilePictureUri",
googleIdTokenCredential.profilePictureUri?.toString()
)
ret.put("phoneNumber", googleIdTokenCredential.phoneNumber)
invoke.resolve(ret)
} catch (e: GoogleIdTokenParsingException) {
invoke.reject("Invalid Google ID token response: ${e.message}")
}
} else {
invoke.reject("Unexpected credential type: ${credential.type}")
}
}
else -> {
invoke.reject("Unexpected credential type")
}
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
const COMMANDS: &[&str] = &["autentikasi"];
const COMMANDS: &[&str] = &["masuk", "keluar"];
fn main() {
tauri_plugin::Builder::new(COMMANDS)
+11 -3
View File
@@ -1,5 +1,13 @@
import { invoke } from '@tauri-apps/api/core'
import { invoke } from "@tauri-apps/api/core";
export async function autentikasi(): Promise<[null]> {
return await invoke<[null]>("");
export type SAuthMuatanMasuk = {
"id-klien": string;
};
export async function masuk(muatan: SAuthMuatanMasuk): Promise<null> {
return await invoke<null>("plugin:auth|masuk", { payload: muatan });
}
export async function keluar(): Promise<null> {
return await invoke<null>("plugin:auth|masuk");
}
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "tauri-plugin-auth-api",
"name": "@segubi/tauri-plugin-auth-api",
"version": "0.1.0",
"author": "Rizky Denianto",
"description": "",
"description": "Program sederhana untuk interaksi dengan autentikasi aplikasi tauri di berbagai sistem operasi",
"type": "module",
"types": "./dist-js/index.d.ts",
"main": "./dist-js/index.cjs",
@@ -1,13 +0,0 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-autentikasi"
description = "Enables the autentikasi command without any pre-configured scope."
commands.allow = ["autentikasi"]
[[permission]]
identifier = "deny-autentikasi"
description = "Denies the autentikasi command without any pre-configured scope."
commands.deny = ["autentikasi"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-keluar"
description = "Enables the keluar command without any pre-configured scope."
commands.allow = ["keluar"]
[[permission]]
identifier = "deny-keluar"
description = "Denies the keluar command without any pre-configured scope."
commands.deny = ["keluar"]
@@ -0,0 +1,13 @@
# Automatically generated - DO NOT EDIT!
"$schema" = "../../schemas/schema.json"
[[permission]]
identifier = "allow-masuk"
description = "Enables the masuk command without any pre-configured scope."
commands.allow = ["masuk"]
[[permission]]
identifier = "deny-masuk"
description = "Denies the masuk command without any pre-configured scope."
commands.deny = ["masuk"]
+32 -5
View File
@@ -4,7 +4,8 @@ Default permissions for the plugin
#### This default permission set includes the following:
- `allow-autentikasi`
- `allow-masuk`
- `allow-keluar`
## Permission Table
@@ -18,12 +19,12 @@ Default permissions for the plugin
<tr>
<td>
`auth:allow-autentikasi`
`auth:allow-keluar`
</td>
<td>
Enables the autentikasi command without any pre-configured scope.
Enables the keluar command without any pre-configured scope.
</td>
</tr>
@@ -31,12 +32,38 @@ Enables the autentikasi command without any pre-configured scope.
<tr>
<td>
`auth:deny-autentikasi`
`auth:deny-keluar`
</td>
<td>
Denies the autentikasi command without any pre-configured scope.
Denies the keluar command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`auth:allow-masuk`
</td>
<td>
Enables the masuk command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`auth:deny-masuk`
</td>
<td>
Denies the masuk command without any pre-configured scope.
</td>
</tr>
+4 -1
View File
@@ -1,3 +1,6 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-autentikasi"]
permissions = [
"allow-masuk",
"allow-keluar"
]
+20 -8
View File
@@ -295,22 +295,34 @@
"type": "string",
"oneOf": [
{
"description": "Enables the autentikasi command without any pre-configured scope.",
"description": "Enables the keluar command without any pre-configured scope.",
"type": "string",
"const": "allow-autentikasi",
"markdownDescription": "Enables the autentikasi command without any pre-configured scope."
"const": "allow-keluar",
"markdownDescription": "Enables the keluar command without any pre-configured scope."
},
{
"description": "Denies the autentikasi command without any pre-configured scope.",
"description": "Denies the keluar command without any pre-configured scope.",
"type": "string",
"const": "deny-autentikasi",
"markdownDescription": "Denies the autentikasi command without any pre-configured scope."
"const": "deny-keluar",
"markdownDescription": "Denies the keluar command without any pre-configured scope."
},
{
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-autentikasi`",
"description": "Enables the masuk command without any pre-configured scope.",
"type": "string",
"const": "allow-masuk",
"markdownDescription": "Enables the masuk command without any pre-configured scope."
},
{
"description": "Denies the masuk command without any pre-configured scope.",
"type": "string",
"const": "deny-masuk",
"markdownDescription": "Denies the masuk command without any pre-configured scope."
},
{
"description": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-masuk`\n- `allow-keluar`",
"type": "string",
"const": "default",
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-autentikasi`"
"markdownDescription": "Default permissions for the plugin\n#### This default permission set includes:\n\n- `allow-masuk`\n- `allow-keluar`"
}
]
}
+8 -2
View File
@@ -2,8 +2,14 @@ use tauri::{AppHandle, Runtime, command};
use crate::AuthExt;
use crate::Result;
use crate::models::SAuthMuatanMasuk;
#[command]
pub(crate) async fn autentikasi<R: Runtime>(app: AppHandle<R>) -> Result<Vec<()>> {
return app.auth().autentikasi();
pub(crate) async fn masuk<R: Runtime>(app: AppHandle<R>, payload: SAuthMuatanMasuk) -> Result<()> {
return app.auth().masuk(payload);
}
#[command]
pub(crate) async fn keluar<R: Runtime>(app: AppHandle<R>) -> Result<()> {
return app.auth().keluar();
}
+8 -2
View File
@@ -1,6 +1,8 @@
use serde::de::DeserializeOwned;
use tauri::{AppHandle, Runtime, plugin::PluginApi};
use crate::models::SAuthMuatanMasuk;
pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
@@ -12,7 +14,11 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
pub struct Auth<R: Runtime>(AppHandle<R>);
impl<R: Runtime> Auth<R> {
pub fn autentikasi(&self) -> crate::Result<Vec<()>> {
return Ok(vec![]);
pub fn masuk(&self, muatan: SAuthMuatanMasuk) -> crate::Result<()> {
return Ok(());
}
pub fn keluar(&self) -> crate::Result<()> {
return Ok(());
}
}
+3 -1
View File
@@ -19,6 +19,8 @@ use desktop::Auth;
#[cfg(mobile)]
use mobile::Auth;
use crate::commands::{keluar, masuk};
/// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the auth APIs.
pub trait AuthExt<R: Runtime> {
fn auth(&self) -> &Auth<R>;
@@ -33,7 +35,7 @@ impl<R: Runtime, T: Manager<R>> crate::AuthExt<R> for T {
/// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("auth")
.invoke_handler(tauri::generate_handler![commands::autentikasi])
.invoke_handler(tauri::generate_handler![masuk, keluar])
.setup(|app, api| {
#[cfg(mobile)]
let auth = mobile::init(app, api)?;
+10 -3
View File
@@ -15,7 +15,7 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
api: PluginApi<R, C>,
) -> crate::Result<Auth<R>> {
#[cfg(target_os = "android")]
let handle = api.register_android_plugin("", "ExamplePlugin")?;
let handle = api.register_android_plugin("com.plugin.auth", "AuthPlugin")?;
#[cfg(target_os = "ios")]
let handle = api.register_ios_plugin(init_plugin_auth)?;
Ok(Auth(handle))
@@ -25,7 +25,14 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
pub struct Auth<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> Auth<R> {
pub fn autentikasi(&self) -> crate::Result<Vec<()>> {
return self.0.run_mobile_plugin("autentikasi").map_err(Into::into);
pub fn masuk(&self, payload: SAuthMuatanMasuk) -> crate::Result<()> {
return self
.0
.run_mobile_plugin("masuk", payload)
.map_err(Into::into);
}
pub fn keluar(&self) -> crate::Result<Vec<()>> {
return self.0.run_mobile_plugin("keluar", ()).map_err(Into::into);
}
}
+6
View File
@@ -1 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SAuthMuatanMasuk {
pub id_klien: String,
}